-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds the `Preferences` standard library; a way to store a TOML-serializable dictionary into top-level `Project.toml` files, then force recompilation of child projects when the preferences are modified. This pull request adds the `Preferences` standard library, which does the actual writing to `Project.toml` files, as well as modifies the loading code to check whether the preferences have changed.
- Loading branch information
1 parent
8bdf569
commit cdd868f
Showing
13 changed files
with
466 additions
and
8 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
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 |
---|---|---|
|
@@ -47,6 +47,7 @@ let | |
:Distributed, | ||
:SharedArrays, | ||
:TOML, | ||
:Preferences, | ||
:Artifacts, | ||
:Pkg, | ||
:Test, | ||
|
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
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,12 @@ | ||
name = "Preferences" | ||
uuid = "21216c6a-2e73-6563-6e65-726566657250" | ||
|
||
[deps] | ||
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" | ||
|
||
[extras] | ||
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" | ||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" | ||
|
||
[targets] | ||
test = ["Test", "Pkg"] |
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,46 @@ | ||
# Preferences | ||
|
||
!!! compat "Julia 1.6" | ||
Julia's `Preferences` API requires at least Julia 1.6. | ||
|
||
Preferences support embedding a simple `Dict` of metadata for a package on a per-project basis. These preferences allow for packages to set simple, persistent pieces of data that the user has selected, that can persist across multiple versions of a package. | ||
|
||
## API Overview | ||
|
||
`Preferences` are used primarily through the `@load_preferences`, `@save_preferences` and `@modify_preferences` macros. These macros will auto-detect the UUID of the calling package, throwing an error if the calling module does not belong to a package. The function forms can be used to load, save or modify preferences belonging to another package. | ||
|
||
Example usage: | ||
|
||
```julia | ||
using Preferences | ||
|
||
function get_preferred_backend() | ||
prefs = @load_preferences() | ||
return get(prefs, "backend", "native") | ||
end | ||
|
||
function set_backend(new_backend) | ||
@modify_preferences!() do prefs | ||
prefs["backend"] = new_backend | ||
end | ||
end | ||
``` | ||
|
||
By default, preferences are stored within the `Project.toml` file of the currently-active project, and as such all new projects will start from a blank state, with all preferences being un-set. | ||
Package authors that wish to have a default value set for their preferences should use the `get(prefs, key, default)` pattern as shown in the code example above. | ||
|
||
# API Reference | ||
|
||
!!! compat "Julia 1.6" | ||
Julia's `Preferences` API requires at least Julia 1.6. | ||
|
||
```@docs | ||
Preferences.load_preferences | ||
Preferences.@load_preferences | ||
Preferences.save_preferences! | ||
Preferences.@save_preferences! | ||
Preferences.modify_preferences! | ||
Preferences.@modify_preferences! | ||
Preferences.clear_preferences! | ||
Preferences.@clear_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,171 @@ | ||
module Preferences | ||
using TOML | ||
using Base: UUID | ||
|
||
export load_preferences, @load_preferences, | ||
save_preferences!, @save_preferences!, | ||
modify_preferences!, @modify_preferences!, | ||
clear_preferences!, @clear_preferences! | ||
|
||
# Helper function to get the UUID of a module, throwing an error if it can't. | ||
function get_uuid(m::Module) | ||
uuid = Base.PkgId(m).uuid | ||
if uuid === nothing | ||
throw(ArgumentError("Module does not correspond to a loaded package!")) | ||
end | ||
return uuid | ||
end | ||
|
||
|
||
""" | ||
load_preferences(uuid_or_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) | ||
prefs = Dict{String,Any}() | ||
|
||
# Finally, load from the currently-active project: | ||
proj_path = Base.active_project() | ||
if isfile(proj_path) | ||
project = TOML.parsefile(proj_path) | ||
if haskey(project, "preferences") && isa(project["preferences"], Dict) | ||
prefs = get(project["preferences"], string(uuid), Dict()) | ||
end | ||
end | ||
return prefs | ||
end | ||
load_preferences(m::Module) = load_preferences(get_uuid(m)) | ||
|
||
|
||
""" | ||
save_preferences!(uuid_or_module, prefs::Dict) | ||
Save the preferences for the given package. Most users should use 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) | ||
# Save to Project.toml | ||
proj_path = Base.active_project() | ||
mkpath(dirname(proj_path)) | ||
project = Dict{String,Any}() | ||
if isfile(proj_path) | ||
project = TOML.parsefile(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 | ||
return nothing | ||
end | ||
function save_preferences!(m::Module, prefs::Dict) | ||
return save_preferences!(get_uuid(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`. | ||
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) | ||
f(prefs) | ||
save_preferences!(uuid, prefs) | ||
return prefs | ||
end | ||
modify_preferences!(f::Function, m::Module) = modify_preferences!(f, get_uuid(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) | ||
# Clear the project preferences key, if it exists | ||
proj_path = Base.active_project() | ||
if isfile(proj_path) | ||
project = TOML.parsefile(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 | ||
|
||
|
||
""" | ||
@load_preferences() | ||
Convenience macro to call `load_preferences()` for the current package. | ||
""" | ||
macro load_preferences() | ||
return quote | ||
load_preferences($(esc(get_uuid(__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(__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(__module__)))) | ||
end | ||
end | ||
|
||
|
||
""" | ||
@clear_preferences!() | ||
Convenience macro to call `clear_preferences!()` for the current package. | ||
""" | ||
macro clear_preferences!() | ||
return quote | ||
preferences!($(esc(get_uuid(__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,12 @@ | ||
name = "UsesPreferences" | ||
uuid = "056c4eb5-4491-6b91-3d28-8fffe3ee2af9" | ||
version = "0.1.0" | ||
|
||
[deps] | ||
Preferences = "21216c6a-2e73-6563-6e65-726566657250" | ||
|
||
[extras] | ||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" | ||
|
||
[targets] | ||
test = ["Test"] |
Oops, something went wrong.