Skip to content

Commit

Permalink
WIP. moving to other machine
Browse files Browse the repository at this point in the history
  • Loading branch information
staticfloat committed May 22, 2020
1 parent a1e817b commit 5f498bb
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 37 deletions.
16 changes: 16 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!
```
130 changes: 108 additions & 22 deletions src/Preferences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

"""
Expand All @@ -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)
Expand All @@ -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

"""
Expand All @@ -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)
Expand All @@ -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

"""
Expand All @@ -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
Expand Down
46 changes: 42 additions & 4 deletions test/preferences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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

Expand Down
22 changes: 11 additions & 11 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5f498bb

Please sign in to comment.