Skip to content

Commit

Permalink
fix #17997, don't load packages in Main
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson committed Sep 7, 2017
1 parent 75ec2b9 commit 1246ab3
Show file tree
Hide file tree
Showing 20 changed files with 342 additions and 253 deletions.
24 changes: 11 additions & 13 deletions base/interactiveutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ end
Return an array of methods with an argument of type `typ`.
The optional second argument restricts the search to a particular module or function
(the default is all modules, starting from Main).
(the default is all modules).
If optional `showparents` is `true`, also return arguments with a parent type of `typ`,
excluding type `Any`.
Expand All @@ -589,30 +589,29 @@ function methodswith(t::Type, f::Function, showparents::Bool=false, meths = Meth
return meths
end

function methodswith(t::Type, m::Module, showparents::Bool=false)
function _methodswith(t::Type, m::Module, showparents::Bool, visited = Set{Module}())
push!(visited, m)
meths = Method[]
for nm in names(m)
if isdefined(m, nm)
f = getfield(m, nm)
if isa(f, Function)
methodswith(t, f, showparents, meths)
elseif isa(f, Module) && !in(f, visited)
append!(meths, _methodswith(t, f::Module, showparents, visited))
end
end
end
return unique(meths)
end

methodswith(t::Type, m::Module, showparents::Bool=false) = _methodswith(t, m, showparents)

function methodswith(t::Type, showparents::Bool=false)
meths = Method[]
mainmod = Main
# find modules in Main
for nm in names(mainmod)
if isdefined(mainmod, nm)
mod = getfield(mainmod, nm)
if isa(mod, Module)
append!(meths, methodswith(t, mod, showparents))
end
end
visited = Set{Module}()
for mod in loaded_modules_array()
append!(meths, _methodswith(t, mod, showparents, visited))
end
return unique(meths)
end
Expand Down Expand Up @@ -679,8 +678,7 @@ download(url, filename)
workspace()
Replace the top-level module (`Main`) with a new one, providing a clean workspace. The
previous `Main` module is made available as `LastMain`. A previously-loaded package can be
accessed using a statement such as `using LastMain.Package`.
previous `Main` module is made available as `LastMain`.
This function should only be used interactively.
"""
Expand Down
104 changes: 83 additions & 21 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
require(Symbol(name))
end
end
Expand Down Expand Up @@ -315,25 +316,86 @@ all platforms, including those with case-insensitive filesystems like macOS and
Windows.
"""
function require(mod::Symbol)
_require(mod)
existed = root_module_exists(mod)
M = _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)
end
_require(mod)
nothing
end
end
end
for callback in package_callbacks
invokelatest(callback, mod)
if !existed
for callback in package_callbacks
invokelatest(callback, mod)
end
end
return M
end

const loaded_modules = ObjectIdDict()
const module_keys = ObjectIdDict()

function register_root_module(key, m::Module)
if haskey(loaded_modules, key)
oldm = loaded_modules[key]
if oldm !== m
name = module_name(oldm)
warn("replacing module $name.")
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

function _require(mod::Symbol)
if root_module_exists(mod)
return root_module(mod)
end
# dependency-tracking is only used for one top-level include(path),
# and is not applied recursively to imported modules:
old_track_dependencies = _track_dependencies[]
Expand All @@ -345,7 +407,7 @@ function _require(mod::Symbol)
if loading !== false
# load already in progress for this module
wait(loading)
return
return root_module(mod)
end
package_locks[mod] = Condition()

Expand All @@ -364,7 +426,8 @@ function _require(mod::Symbol)
if JLOptions().use_compilecache != 0
doneprecompile = _require_search_from_serialized(mod, path)
if !isa(doneprecompile, Bool)
return # success
register_all(doneprecompile)
return root_module(mod) # success
end
end

Expand All @@ -391,14 +454,17 @@ 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 root_module(mod) # success
end
end

# just load the file normally via include
# for unknown dependencies
local M
try
Base.include_relative(Main, path)
Base.include_relative(__toplevel__, path)
return root_module(mod)
catch ex
if doneprecompile === true || JLOptions().use_compilecache == 0 || !precompilableerror(ex, true)
rethrow() # rethrow non-precompilable=true errors
Expand All @@ -411,6 +477,8 @@ 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)
return root_module(mod)
end
finally
toplevel_load[] = last
Expand Down Expand Up @@ -532,7 +600,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
Expand Down Expand Up @@ -570,15 +638,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
Expand Down Expand Up @@ -675,7 +737,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)
Expand Down
2 changes: 1 addition & 1 deletion base/pkg/entry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ function resolve(
info("$(up)grading $pkg: v$ver1 => v$ver2")
Write.update(pkg, Read.sha1(pkg,ver2))
pkgsym = Symbol(pkg)
if Base.isbindingresolved(Main, pkgsym) && isa(getfield(Main, pkgsym), Module)
if Base.root_module_exists(pkgsym)
push!(imported, "- $pkg")
end
end
Expand Down
31 changes: 21 additions & 10 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,17 @@ julia> fullname(Main)
```
"""
function fullname(m::Module)
m === Main && return ()
m === Base && return (:Base,) # issue #10653
mn = module_name(m)
if m === Main || m === Base || m === Core
return (mn,)
end
mp = module_parent(m)
if mp === m
# not Main, but is its own parent, means a prior Main module
n = ()
if mn !== :Main
return (mn,)
end
# top-level module, not Main, called :Main => prior Main module
n = (:Main,)
this = Main
while this !== m
if isdefined(this, :LastMain)
Expand Down Expand Up @@ -516,15 +520,22 @@ function _subtypes(m::Module, x::Union{DataType,UnionAll},
end
return sts
end
function subtypes(m::Module, x::Union{DataType,UnionAll})
if isabstract(x)
sort!(collect(_subtypes(m, x)), by=string)
else

function _subtypes_in(mods::Array, x::Union{DataType,UnionAll})
if !isabstract(x)
# Fast path
Union{DataType,UnionAll}[]
return Union{DataType,UnionAll}[]
end
sts = Set{Union{DataType,UnionAll}}()
visited = Set{Module}()
for m in mods
_subtypes(m, x, sts, visited)
end
return sort!(collect(sts), by=string)
end

subtypes(m::Module, x::Union{DataType,UnionAll}) = _subtypes_in([m], x)

"""
subtypes(T::DataType)
Expand All @@ -541,7 +552,7 @@ julia> subtypes(Integer)
Unsigned
```
"""
subtypes(x::Union{DataType,UnionAll}) = subtypes(Main, x)
subtypes(x::Union{DataType,UnionAll}) = _subtypes_in(loaded_modules_array(), x)

function to_tuple_type(@nospecialize(t))
@_pure_meta
Expand Down
29 changes: 17 additions & 12 deletions base/serialize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,10 @@ function serialize(s::AbstractSerializer, d::Dict)
end

function serialize_mod_names(s::AbstractSerializer, m::Module)
p = module_parent(m)
if m !== p
serialize_mod_names(s, p)
if Base.is_root_module(m)
serialize(s, Base.root_module_key(m))
else
serialize_mod_names(s, module_parent(m))
serialize(s, module_name(m))
end
end
Expand Down Expand Up @@ -772,21 +773,25 @@ function deserialize_svec(s::AbstractSerializer)
end

function deserialize_module(s::AbstractSerializer)
path = deserialize(s)
m = Main
if isa(path,Tuple) && path !== ()
# old version
for mname in path
m = getfield(m,mname)::Module
mkey = deserialize(s)
if isa(mkey, Tuple)
# old version, TODO: remove
if mkey === ()
return Main
end
m = Base.root_module(mkey[1])
for i = 2:length(mkey)
m = getfield(m, mkey[i])::Module
end
else
mname = path
m = Base.root_module(mkey)
mname = deserialize(s)
while mname !== ()
m = getfield(m,mname)::Module
m = getfield(m, mname)::Module
mname = deserialize(s)
end
end
m
return m
end

function deserialize(s::AbstractSerializer, ::Type{Method})
Expand Down
4 changes: 2 additions & 2 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ function show(io::IO, p::Pair)
end

function show(io::IO, m::Module)
if m === Main
print(io, "Main")
if is_root_module(m)
print(io, module_name(m))
else
print(io, join(fullname(m),"."))
end
Expand Down
6 changes: 5 additions & 1 deletion examples/clustermanager/0mq/ZMQCM.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

using ZMQ
# the 0mq clustermanager depends on package ZMQ. For testing purposes, at least
# make sure the code loads without it.
try
using ZMQ
end

import Base: launch, manage, connect, kill

Expand Down
Loading

0 comments on commit 1246ab3

Please sign in to comment.