-
-
Notifications
You must be signed in to change notification settings - Fork 262
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Preferences provides a simple package configuration store; packages can store arbitrary configurations into `Dict` objects that get serialized into TOML files and stored within the `prefs` folder of a Julia depot.
- Loading branch information
1 parent
6679131
commit a1e817b
Showing
7 changed files
with
270 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
module Preferences | ||
import ...Pkg, ..TOML | ||
import ..API: get_uuid | ||
import ..Types: parse_toml | ||
import Base: UUID | ||
|
||
export load_preferences, @load_preferences, | ||
save_preferences!, @save_preferences!, | ||
modify_preferences!, @modify_preferences!, | ||
clear_preferences!, @clear_preferences! | ||
|
||
|
||
""" | ||
preferences_path(uuid::UUID) | ||
Return the path of the preferences file for the given package `UUID`. | ||
""" | ||
function preferences_path(uuid::UUID) | ||
return joinpath(Pkg.depots1(), "prefs", string(uuid, ".toml")) | ||
end | ||
|
||
""" | ||
get_uuid_throw(m::Module) | ||
Convert a `Module` to a `UUID`, throwing an `ArgumentError` if the given module does not | ||
correspond to a loaded package. This is expected for modules such as `Base`, `Main`, | ||
anonymous modules, etc... | ||
""" | ||
function get_uuid_throw(m::Module) | ||
uuid = get_uuid(m) | ||
if uuid === nothing | ||
throw(ArgumentError("Module does not correspond to a loaded package!")) | ||
end | ||
return uuid | ||
end | ||
|
||
""" | ||
load_preferences(uuid::UUID) | ||
load_preferences(m::Module) | ||
Load the preferences for the given package, returning them as a `Dict`. Most users | ||
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}() | ||
end | ||
return parse_toml(path) | ||
end | ||
load_preferences(m::Module) = load_preferences(get_uuid_throw(m)) | ||
|
||
""" | ||
save_preferences!(uuid::UUID, prefs::Dict) | ||
save_preferences!(m::Module, prefs::Dict) | ||
Save the preferences for the given package. Most users should use the | ||
`@load_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) | ||
end | ||
return nothing | ||
end | ||
function save_preferences!(m::Module, prefs::Dict) | ||
return save_preferences!(get_uuid_throw(m), prefs) | ||
end | ||
|
||
""" | ||
modify_preferences!(f::Function, uuid::UUID) | ||
modify_preferences!(f::Function, m::Module) | ||
Supports `do`-block modification of preferences. Loads the preferences, passes them to a | ||
user function, then writes the modified `Dict` back to the preferences file. Example: | ||
```julia | ||
modify_preferences!(@__MODULE__) do prefs | ||
prefs["key"] = "value" | ||
end | ||
``` | ||
This function returns the full preferences object. Most users should use the | ||
`@modify_preferences!()` macro which auto-determines the calling `Module`. | ||
""" | ||
function modify_preferences!(f::Function, uuid::UUID) | ||
prefs = load_preferences(uuid) | ||
f(prefs) | ||
save_preferences!(uuid, prefs) | ||
return prefs | ||
end | ||
modify_preferences!(f::Function, m::Module) = modify_preferences!(f, get_uuid_throw(m)) | ||
|
||
""" | ||
clear_preferences!(uuid::UUID) | ||
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`. | ||
""" | ||
function clear_preferences!(uuid::UUID) | ||
rm(preferences_path(uuid); force=true) | ||
end | ||
|
||
""" | ||
@load_preferences() | ||
Convenience macro to call `load_preferences()` for the current package. | ||
""" | ||
macro load_preferences() | ||
return quote | ||
load_preferences($(esc(get_uuid_throw(__module__)))) | ||
end | ||
end | ||
|
||
""" | ||
@save_preferences!(prefs) | ||
Convenience macro to call `save_preferences!()` for the current package. | ||
""" | ||
macro save_preferences!(prefs) | ||
return quote | ||
save_preferences!($(esc(get_uuid_throw(__module__))), $(esc(prefs))) | ||
end | ||
end | ||
|
||
""" | ||
@modify_preferences!(func) | ||
Convenience macro to call `modify_preferences!()` for the current package. | ||
""" | ||
macro modify_preferences!(func) | ||
return quote | ||
modify_preferences!($(esc(func)), $(esc(get_uuid_throw(__module__)))) | ||
end | ||
end | ||
|
||
""" | ||
@clear_preferences!() | ||
Convenience macro to call `clear_preferences!()` for the current package. | ||
""" | ||
macro clear_preferences!() | ||
return quote | ||
preferences!($(esc(get_uuid_throw(__module__)))) | ||
end | ||
end | ||
end # module Preferences |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
module PreferencesTests | ||
import ..Pkg | ||
using ..Utils, ..Pkg.TOML | ||
using Test, Pkg.Preferences | ||
|
||
@testset "Preferences" begin | ||
|
||
temp_pkg_dir() do project_dir | ||
# Test creation of preferences within this temporary depot | ||
uuid = Base.UUID(UInt128(0)) | ||
toml_path = Pkg.Preferences.preferences_path(uuid) | ||
|
||
@test isempty(load_preferences(uuid)) | ||
@test !isfile(toml_path) | ||
|
||
# Now, save something | ||
save_preferences!(uuid, Dict("foo" => "bar")) | ||
@show toml_path | ||
@test isfile(toml_path) | ||
prefs = load_preferences(uuid) | ||
@test load_preferences(uuid)["foo"] == "bar" | ||
|
||
prefs = modify_preferences!(uuid) do prefs | ||
prefs["foo"] = "baz" | ||
prefs["spoon"] = [Dict("qux" => "idk")] | ||
end | ||
@test prefs == load_preferences(uuid) | ||
end | ||
|
||
# Do a test within a package to ensure that we can use the macros | ||
temp_pkg_dir() do project_dir | ||
add_this_pkg() | ||
copy_test_package(project_dir, "UsesPreferences") | ||
Pkg.develop(path=joinpath(project_dir, "UsesPreferences")) | ||
Pkg.test("UsesPreferences") | ||
|
||
up_uuid = Base.UUID("056c4eb5-4491-6b91-3d28-8fffe3ee2af9") | ||
prefs = load_preferences(up_uuid) | ||
@test haskey(prefs, "backend") | ||
@test prefs["backend"] == "jlFPGA" | ||
end | ||
end | ||
|
||
end # module PreferencesTests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
name = "UsesPreferences" | ||
uuid = "056c4eb5-4491-6b91-3d28-8fffe3ee2af9" | ||
version = "0.1.0" | ||
|
||
[deps] | ||
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" | ||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
module UsesPreferences | ||
using Pkg.Preferences | ||
|
||
# This will get initialized in __init__() | ||
backend = Ref{String}() | ||
|
||
function set_backend(new_backend::AbstractString) | ||
if !(new_backend in ("OpenCL", "CUDA", "jlFPGA")) | ||
throw(ArgumentError("Invalid backend: \"$(new_backend)\"")) | ||
end | ||
|
||
# Set it in our runtime values, as well as saving it to disk | ||
backend[] = new_backend | ||
@modify_preferences!() do prefs | ||
prefs["backend"] = new_backend | ||
end | ||
end | ||
|
||
function get_backend() | ||
return backend[] | ||
end | ||
|
||
function __init__() | ||
@modify_preferences!() do prefs | ||
prefs["initialized"] = "true" | ||
|
||
# If it's never been set before, default it to OpenCL | ||
prefs["backend"] = get(prefs, "backend", "OpenCL") | ||
backend[] = prefs["backend"] | ||
end | ||
end | ||
|
||
end # module UsesPreferences |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using UsesPreferences, Test, Pkg, Pkg.Preferences | ||
|
||
# Get the UUID for UsesPreferences | ||
up_uuid = Pkg.API.get_uuid(UsesPreferences) | ||
|
||
@show Pkg.Preferences.preferences_path(up_uuid) | ||
|
||
prefs = load_preferences(up_uuid) | ||
@test haskey(prefs, "backend") | ||
@test prefs["backend"] == "OpenCL" | ||
@test UsesPreferences.get_backend() == "OpenCL" | ||
|
||
UsesPreferences.set_backend("CUDA") | ||
prefs = load_preferences(up_uuid) | ||
@test haskey(prefs, "backend") | ||
@test prefs["backend"] == "CUDA" | ||
@test UsesPreferences.get_backend() == "CUDA" | ||
|
||
# sorry, AMD | ||
@test_throws ArgumentError UsesPreferences.set_backend("ROCm") | ||
prefs = load_preferences(up_uuid) | ||
@test haskey(prefs, "backend") | ||
@test prefs["backend"] == "CUDA" | ||
@test UsesPreferences.get_backend() == "CUDA" | ||
|
||
clear_preferences!(up_uuid) | ||
prefs = load_preferences(up_uuid) | ||
@test !haskey(prefs, "backend") | ||
@test UsesPreferences.get_backend() == "CUDA" | ||
|
||
# And finally, save something back so that the parent process can read it: | ||
UsesPreferences.set_backend("jlFPGA") |