diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 00000000..cc612af6 --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,91 @@ +# This file is machine-generated - editing it directly is not advised + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[CodeTracking]] +deps = ["InteractiveUtils", "Test", "UUIDs"] +git-tree-sha1 = "7e19dccd5667e0a8d9327ac76966977dbc0df85f" +uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" +version = "0.5.0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JuliaInterpreter]] +deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"] +git-tree-sha1 = "8aeee3235a4c995493475be2cc27e26ab234c72e" +repo-rev = "master" +repo-url = "https://github.com/JuliaDebug/JuliaInterpreter.jl.git" +uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a" +version = "0.4.0" + +[[LibGit2]] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[LoweredCodeUtils]] +deps = ["JuliaInterpreter", "Test"] +git-tree-sha1 = "51e3d512848f96e0017338e1e7e3485ee2730e6e" +uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" +version = "0.3.0" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[OrderedCollections]] +deps = ["Random", "Serialization", "Test"] +git-tree-sha1 = "85619a3f3e17bb4761fe1b1fd47f0e979f964d5b" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.0.2" + +[[Pkg]] +deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/Project.toml b/Project.toml index 061ca09e..ab244ac0 100644 --- a/Project.toml +++ b/Project.toml @@ -12,18 +12,18 @@ LoweredCodeUtils = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [compat] CodeTracking = "0.4, 0.5" -JuliaInterpreter = "0.2, 0.3" +JuliaInterpreter = "0.2, 0.3, 0.4" LoweredCodeUtils = "0.3" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -Example = "7876af07-990d-54b4-ab0e-23690620f79a" EponymTuples = "97e2ac4a-e175-5f49-beb1-4d6866a6cdc3" +Example = "7876af07-990d-54b4-ab0e-23690620f79a" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "Example", "EponymTuples"] diff --git a/REQUIRE b/REQUIRE index 614e1668..e72f1e74 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,5 +1,5 @@ julia 1.0 OrderedCollections CodeTracking 0.4 0.6 -JuliaInterpreter 0.2 0.4 +JuliaInterpreter 0.2 0.5 LoweredCodeUtils 0.3 0.4 diff --git a/src/Revise.jl b/src/Revise.jl index 7a79c3f5..b01044be 100644 --- a/src/Revise.jl +++ b/src/Revise.jl @@ -277,6 +277,7 @@ function eval_new!(exs_sigs_new::ExprsSigs, exs_sigs_old, mod::Module) # try sigs = eval_with_signatures(mod, ex) # All signatures defined by `ex` for p in workers() + p == myid() && continue try # don't error if `mod` isn't defined on the worker remotecall(Core.eval, p, mod, ex) catch @@ -337,18 +338,18 @@ push_expr!(methodinfo::CodeTrackingMethodInfo, mod::Module, ex::Expr) = (push!(m pop_expr!(methodinfo::CodeTrackingMethodInfo) = (pop!(methodinfo.exprstack); methodinfo) # Eval and insert into CodeTracking data -function eval_with_signatures(mod, ex::Expr; define=true) +function eval_with_signatures(mod, ex::Expr; define=true, kwargs...) methodinfo = CodeTrackingMethodInfo(ex) docexprs = Dict{Module,Vector{Expr}}() - methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; define=define) + methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; define=define, kwargs...) return methodinfo.allsigs end -function instantiate_sigs!(modexsigs::ModuleExprsSigs) +function instantiate_sigs!(modexsigs::ModuleExprsSigs; define=false, kwargs...) for (mod, exsigs) in modexsigs for rex in keys(exsigs) is_doc_expr(rex.ex) && continue - sigs = eval_with_signatures(mod, rex.ex; define=false) + sigs = eval_with_signatures(mod, rex.ex; define=define, kwargs...) exsigs[rex.ex] = sigs end end @@ -579,12 +580,12 @@ Watch `file` for updates and [`revise`](@ref) loaded code with any changes. `mod` is the module into which `file` is evaluated; if omitted, it defaults to `Main`. """ -function track(mod::Module, file::AbstractString) +function track(mod::Module, file::AbstractString; define=false, skip_include=true) isfile(file) || error(file, " is not a file") file = normpath(abspath(file)) fm = parse_source(file, mod) if fm !== nothing - instantiate_sigs!(fm) + instantiate_sigs!(fm; define=define, skip_include=skip_include) id = PkgId(mod) if !haskey(pkgdatas, id) pkgdatas[id] = PkgData(id, pathof(mod)) @@ -603,18 +604,23 @@ track(file::AbstractString) = track(Main, file) """ includet(filename) -Load `filename` and track any future changes to it. -`includet` is deliberately non-recursive, so if `filename` loads any other files, -they will not be automatically tracked. -(See [`Revise.track`](@ref) to set it up manually.) +Load `filename` and track any future changes to it. `includet` is simply shorthand for + + Revise.track(Main, filename; skip_include=false) `includet` is intended for "user scripts," e.g., a file you use locally for a specific -purpose such as loading a specific data set or performing some kind of analysis. -Do not use `includet` for packages, as those should be handled by `using` or `import`. +purpose such as loading a specific data set or performing a particular analysis. +Do *not* use `includet` for packages, as those should be handled by `using` or `import`. +(If you're working with code in Base or one of Julia's standard libraries, use +`Revise.track(mod)` instead, where `mod` is the module.) If `using` and `import` aren't working, you may have packages in a non-standard location; try fixing it with something like `push!(LOAD_PATH, "/path/to/my/private/repos")`. + +`includet` is deliberately non-recursive, so if `filename` loads any other files, +they will not be automatically tracked. +(See [`Revise.track`](@ref) to set it up manually.) """ -includet(file::AbstractString) = (Base.include(Main, file); track(Main, file)) +includet(file::AbstractString) = track(Main, file; define=true, skip_include=false) """ Revise.silence(pkg) diff --git a/src/lowered.jl b/src/lowered.jl index 1ebc1409..ffc6684a 100644 --- a/src/lowered.jl +++ b/src/lowered.jl @@ -26,14 +26,14 @@ add_signature!(methodinfo::MethodInfo, @nospecialize(sig), ln) = push!(methodinf push_expr!(methodinfo::MethodInfo, mod::Module, ex::Expr) = methodinfo pop_expr!(methodinfo::MethodInfo) = methodinfo -function methods_by_execution(mod::Module, ex::Expr; define=true) +function methods_by_execution(mod::Module, ex::Expr; kwargs...) methodinfo = MethodInfo() docexprs = Dict{Module,Vector{Expr}}() - value = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; define=define) + value = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; kwargs...) return methodinfo, docexprs end -function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, mod::Module, ex::Expr; define=true) +function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, mod::Module, ex::Expr; kwargs...) # We have to turn off all active breakpoints, https://github.com/timholy/CodeTracking.jl/issues/27 bps = filter(bp->bp[].isactive, JuliaInterpreter._breakpoints) for bp in bps @@ -43,7 +43,7 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, mod try frame = prepare_thunk(mod, ex) frame === nothing && return nothing - ret = methods_by_execution!(recurse, methodinfo, docexprs, frame; define=define) + ret = methods_by_execution!(recurse, methodinfo, docexprs, frame; kwargs...) catch err isa(err, InterruptException) && rethrow(err) @warn "error evaluating in module $mod: $ex" exception=(err, catch_backtrace()) @@ -56,7 +56,7 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, mod return ret end -function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, frame; define=true) +function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, frame; define=true, skip_include=true) mod = moduleof(frame) signatures = [] # temporary for method signature storage pc = frame.pc @@ -141,7 +141,7 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, fra end assign_this!(frame, value) pc = next_or_nothing!(frame) - elseif f === getfield(mod, :include) || f === Base.include || f === Core.include + elseif skip_include && (f === getfield(mod, :include) || f === Base.include || f === Core.include) # Skip include calls, otherwise we load new code assign_this!(frame, nothing) # FIXME: the file might return something different from `nothing` pc = next_or_nothing!(frame) diff --git a/src/pkgs.jl b/src/pkgs.jl index d389513f..86d2dadf 100644 --- a/src/pkgs.jl +++ b/src/pkgs.jl @@ -212,7 +212,7 @@ function maybe_parse_from_cache!(pkgdata::PkgData, file::AbstractString) return add_definitions_from_repl(file) end fi = fileinfo(pkgdata, file) - if isempty(fi.modexsigs) + if isempty(fi.modexsigs) && !isempty(fi.cachefile) # Source was never parsed, get it from the precompile cache src = read_from_cache(pkgdata, file) filep = joinpath(basedir(pkgdata), file) diff --git a/test/runtests.jl b/test/runtests.jl index cbeac623..f8968c6f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1395,6 +1395,35 @@ revise_f(x) = 2 includet(srcfile) @test basename(srcfile) ∈ Revise.watched_files[dirname(srcfile)].trackedfiles push!(to_remove, srcfile) + + # Double-execution (issue #263) + srcfile = joinpath(tempdir(), randtmp()*".jl") + open(srcfile, "w") do io + print(io, """ + println("executed") + """) + end + logfile = joinpath(tempdir(), randtmp()*".log") + open(logfile, "w") do io + redirect_stdout(io) do + includet(srcfile) + end + end + lines = readlines(logfile) + @test length(lines) == 1 && chomp(lines[1]) == "executed" + sleep(0.1) + open(srcfile, "w") do io + print(io, """ + println("executed again") + """) + end + open(logfile, "w") do io + redirect_stdout(io) do + yry() + end + end + lines = readlines(logfile) + @test length(lines) == 1 && chomp(lines[1]) == "executed again" end @testset "Auto-track user scripts" begin