From 5f498bbb6150372e83c993b7a3a411fac5b18568 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Fri, 22 May 2020 14:58:45 -0700 Subject: [PATCH] WIP. moving to other machine --- docs/src/api.md | 16 ++++++ src/Preferences.jl | 130 ++++++++++++++++++++++++++++++++++++-------- test/preferences.jl | 46 ++++++++++++++-- test/runtests.jl | 22 ++++---- 4 files changed, 177 insertions(+), 37 deletions(-) diff --git a/docs/src/api.md b/docs/src/api.md index 15814b193c..a4742e5734 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -87,3 +87,19 @@ Pkg.Artifacts.ensure_all_artifacts_installed Pkg.Artifacts.@artifact_str Pkg.Artifacts.archive_artifact ``` + +## [Preferences API Reference](@id Preferences-Reference) + +!!! compat "Julia 1.6" + Pkg's preferences API requires at least Julia 1.6. + +```@docs +Pkg.Preferences.load_preferences +Pkg.Preferences.@load_preferences +Pkg.Preferences.save_preferences! +Pkg.Preferences.@save_preferences! +Pkg.Preferences.modify_preferences! +Pkg.Preferences.@modify_preferences! +Pkg.Preferences.clear_preferences! +Pkg.Preferences.@clear_preferences! +``` \ No newline at end of file diff --git a/src/Preferences.jl b/src/Preferences.jl index 8dff612e79..c8a7368058 100644 --- a/src/Preferences.jl +++ b/src/Preferences.jl @@ -11,12 +11,14 @@ export load_preferences, @load_preferences, """ - preferences_path(uuid::UUID) + depot_preferences_paths(uuid::UUID) -Return the path of the preferences file for the given package `UUID`. +Return the possible paths of all preferences file for the given package `UUID` saved in +depot-wide `prefs` locations. """ -function preferences_path(uuid::UUID) - return joinpath(Pkg.depots1(), "prefs", string(uuid, ".toml")) +function depot_preferences_paths(uuid::UUID) + depots = reverse(Pkg.depots()) + return [joinpath(depot, "prefs", string(uuid, ".toml")) for depot in depots] end """ @@ -34,6 +36,26 @@ function get_uuid_throw(m::Module) return uuid end +""" + recursive_merge(base::Dict, overrides::Dict...) + +Helper function to merge preference dicts recursively, honoring overrides in nested +dictionaries properly. +""" +function recursive_merge(base::Dict, overrides::Dict...) + new_base = Base._typeddict(base, overrides...) + for override in overrides + for (k, v) in override + if haskey(new_base, k) && isa(new_base[k], Dict) && isa(override[k], Dict) + new_base[k] = recursive_merge(new_base[k], override[k]) + else + new_base[k] = override[k] + end + end + end + return new_base +end + """ load_preferences(uuid::UUID) load_preferences(m::Module) @@ -42,33 +64,72 @@ Load the preferences for the given package, returning them as a `Dict`. Most us should use the `@load_preferences()` macro which auto-determines the calling `Module`. """ function load_preferences(uuid::UUID) - path = preferences_path(uuid) - if !isfile(path) - return Dict{String,Any}() + # First, load from depots, merging as we go: + prefs = Dict{String,Any}() + for path in depot_preferences_paths(uuid) + if isfile(path) + prefs = recursive_merge(prefs, parse_toml(path)) + end + end + + # Finally, load from the currently-active project: + proj_path = Base.active_project() + if isfile(proj_path) + project = parse_toml(proj_path) + if haskey(project, "preferences") && isa(project["preferences"], Dict) + proj_prefs = get(project["preferences"], string(uuid), Dict()) + prefs = recursive_merge(prefs, proj_prefs) + end end - return parse_toml(path) + return prefs end load_preferences(m::Module) = load_preferences(get_uuid_throw(m)) """ - save_preferences!(uuid::UUID, prefs::Dict) - save_preferences!(m::Module, prefs::Dict) + save_preferences!(uuid::UUID, prefs::Dict; depot::Union{String,Nothing} = nothing) + save_preferences!(m::Module, prefs::Dict; depot::Union{String,Nothing} = nothing) Save the preferences for the given package. Most users should use the -`@load_preferences()` macro which auto-determines the calling `Module`. See also the +`@save_preferences!()` macro which auto-determines the calling `Module`. See also the `modify_preferences!()` function (and the associated `@modifiy_preferences!()` macro) for easy load/modify/save workflows. -""" -function save_preferences!(uuid::UUID, prefs::Dict) - path = preferences_path(uuid) - mkpath(dirname(path)) - open(path, "w") do io - TOML.print(io, prefs, sorted=true) + +The `depot` keyword argument allows saving of depot-wide preferences, as opposed to the +default of project-specific preferences. Simply set the `depot` keyword argument to the +path of a depot (use `Pkg.depots1()` for the default depot) and the preferences will be +saved to that location. +""" +function save_preferences!(uuid::UUID, prefs::Dict; + depot::Union{AbstractString,Nothing} = nothing) + if depot === nothing + # Save to project + proj_path = Base.active_project() + project = Dict{String,Any}() + if isfile(proj_path) + project = parse_toml(proj_path) + end + if !haskey(project, "preferences") + project["preferences"] = Dict{String,Any}() + end + if !isa(project["preferences"], Dict) + error("$(proj_path) has conflicting `preferences` entry type: Not a Dict!") + end + project["preferences"][string(uuid)] = prefs + open(proj_path, "w") do io + TOML.print(io, project, sorted=true) + end + else + path = joinpath(depot, "prefs", string(uuid, ".toml")) + mkpath(dirname(path)) + open(path, "w") do io + TOML.print(io, prefs, sorted=true) + end end return nothing end -function save_preferences!(m::Module, prefs::Dict) - return save_preferences!(get_uuid_throw(m), prefs) +function save_preferences!(m::Module, prefs::Dict; + depot::Union{AbstractString,Nothing} = nothing) + return save_preferences!(get_uuid_throw(m), prefs; depot=depot) end """ @@ -86,6 +147,9 @@ end This function returns the full preferences object. Most users should use the `@modify_preferences!()` macro which auto-determines the calling `Module`. + +Note that this method does not support modifying depot-wide preferences; modifications +always are saved to the active project. """ function modify_preferences!(f::Function, uuid::UUID) prefs = load_preferences(uuid) @@ -100,10 +164,30 @@ modify_preferences!(f::Function, m::Module) = modify_preferences!(f, get_uuid_th clear_preferences!(m::Module) Convenience method to remove all preferences for the given package. Most users should -use the `@clear_preferences!()` macro, which auto-determines the calling `Module`. +use the `@clear_preferences!()` macro, which auto-determines the calling `Module`. This +method clears not only project-specific preferences, but also depot-wide preferences, if +the current user has the permissions to do so. """ function clear_preferences!(uuid::UUID) - rm(preferences_path(uuid); force=true) + for path in depot_preferences_paths(uuid) + try + rm(path; force=true) + catch + @warn("Unable to remove preference path $(path)") + end + end + + # Clear the project preferences key, if it exists + proj_path = Base.active_project() + if isfile(proj_path) + project = parse_toml(proj_path) + if haskey(project, "preferences") && isa(project["preferences"], Dict) + delete!(project["preferences"], string(uuid)) + open(proj_path, "w") do io + TOML.print(io, project, sorted=true) + end + end + end end """ @@ -120,7 +204,9 @@ end """ @save_preferences!(prefs) -Convenience macro to call `save_preferences!()` for the current package. +Convenience macro to call `save_preferences!()` for the current package. Note that +saving to a depot path is not supported in this macro, use `save_preferences!()` if you +wish to do that. """ macro save_preferences!(prefs) return quote diff --git a/test/preferences.jl b/test/preferences.jl index a247d39c6c..27dff910a4 100644 --- a/test/preferences.jl +++ b/test/preferences.jl @@ -4,18 +4,41 @@ using ..Utils, ..Pkg.TOML using Test, Pkg.Preferences @testset "Preferences" begin + # Create a temporary package, store some preferences within it. + with_temp_env() do project_dir + uuid = Base.UUID(UInt128(0)) + save_preferences!(uuid, Dict("foo" => "bar")) + + project_path = joinpath(project_dir, "Project.toml") + @test isfile(project_path) + proj = Pkg.Types.parse_toml(project_path) + @test haskey(proj, "preferences") + @test isa(proj["preferences"], Dict) + @test haskey(proj["preferences"], string(uuid)) + @test isa(proj["preferences"][string(uuid)], Dict) + @test proj["preferences"][string(uuid)]["foo"] == "bar" + + prefs = modify_preferences!(uuid) do prefs + prefs["foo"] = "baz" + prefs["spoon"] = [Dict("qux" => "idk")] + end + @test prefs == load_preferences(uuid) + + clear_preferences!(uuid) + proj = Pkg.Types.parse_toml(project_path) + @test !haskey(proj, "preferences") + end temp_pkg_dir() do project_dir - # Test creation of preferences within this temporary depot + # Test setting of depot-wide preferences uuid = Base.UUID(UInt128(0)) - toml_path = Pkg.Preferences.preferences_path(uuid) + toml_path = last(Pkg.Preferences.depot_preferences_paths(uuid)) @test isempty(load_preferences(uuid)) @test !isfile(toml_path) # Now, save something - save_preferences!(uuid, Dict("foo" => "bar")) - @show toml_path + save_preferences!(uuid, Dict("foo" => "bar"); depot=Pkg.depots1()) @test isfile(toml_path) prefs = load_preferences(uuid) @test load_preferences(uuid)["foo"] == "bar" @@ -24,7 +47,16 @@ using Test, Pkg.Preferences prefs["foo"] = "baz" prefs["spoon"] = [Dict("qux" => "idk")] end + + # Test that we get the properly-merged prefs, but that the + # depot-wide file stays the same: @test prefs == load_preferences(uuid) + toml_prefs = Pkg.Types.parse_toml(toml_path) + @test toml_prefs["foo"] != prefs["foo"] + @test !haskey(toml_prefs, "spoon") + + clear_preferences!(uuid) + @test !isfile(toml_path) end # Do a test within a package to ensure that we can use the macros @@ -38,6 +70,12 @@ using Test, Pkg.Preferences prefs = load_preferences(up_uuid) @test haskey(prefs, "backend") @test prefs["backend"] == "jlFPGA" + + # Set a new depot-level preference, ensure that it's ignored: + save_preferences!(up_uuid, Dict("backend" => "CUDA"); depot=Pkg.depots1()) + prefs = load_preferences(up_uuid) + @test haskey(prefs, "backend") + @test prefs["backend"] == "jlFPGA" end end diff --git a/test/runtests.jl b/test/runtests.jl index 99149b168f..9e091489ed 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,17 +8,17 @@ import Pkg rm(joinpath(@__DIR__, "registries"); force = true, recursive = true) include("utils.jl") -include("new.jl") -include("pkg.jl") -include("repl.jl") -include("api.jl") -include("registry.jl") -include("subdir.jl") -include("artifacts.jl") -include("binaryplatforms.jl") -include("platformengines.jl") -include("sandbox.jl") -include("resolve.jl") +# include("new.jl") +# include("pkg.jl") +# include("repl.jl") +# include("api.jl") +# include("registry.jl") +# include("subdir.jl") +# include("artifacts.jl") +# include("binaryplatforms.jl") +# include("platformengines.jl") +# include("sandbox.jl") +# include("resolve.jl") include("preferences.jl") # clean up locally cached registry