Skip to content

Commit

Permalink
Merge pull request #6 from DilumAluthge/dpa/delete-preference
Browse files Browse the repository at this point in the history
Add the `@has_preference` and `@delete_preferences` convenience functions and macros
  • Loading branch information
staticfloat committed Jan 7, 2021
2 parents e517d28 + bae9101 commit f52ba25
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- windows-latest
arch:
- x64
- x86
# - x86 # TODO: uncomment this line
exclude:
- os: macOS-latest
arch: x86
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Preferences"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
authors = ["Elliot Saba <elliot.saba@juliacomputing.com>"]
version = "1.0.0"
version = "1.1.0"

[deps]
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Preferences

[![Continuous Integration][ci-img]][ci-url]
[![Code Coverage][codecov-img]][codecov-url]

[ci-url]: https://github.com/JuliaPackaging/Preferences.jl/actions?query=workflow%3ACI
[codecov-url]: https://codecov.io/gh/JuliaPackaging/Preferences.jl

[ci-img]: https://github.com/JuliaPackaging/Preferences.jl/workflows/CI/badge.svg "Continuous Integration"
[codecov-img]: https://codecov.io/gh/JuliaPackaging/Preferences.jl/branch/master/graph/badge.svg "Code Coverage"

The `Preferences` package provides a convenient, integrated way for packages to store configuration switches to persistent TOML files, and use those pieces of information at both run time and compile time.
This enables the user to modify the behavior of a package, and have that choice reflected in everything from run time algorithm choice to code generation at compile time.
Preferences are stored as TOML dictionaries and are, by default, stored within a `(Julia)LocalPreferences.toml` file next to the currently-active project.
Expand All @@ -17,12 +26,16 @@ When your package sets a compile-time preference, it is usually best to suggest

## API

Preferences use is very simple; it is all based around two functions (which each have convenience macros): `@set_preferences!()` and `@load_preference()`.
Preferences use is very simple; it is all based around four functions (which each have convenience macros): `@set_preferences!()`, `@load_preference()`, `@has_preference()`, and `@delete_preferences!()`.

* `@load_preference(key, default = nothing)`: This loads a preference named `key` for the current package. If no such preference is found, it returns `default`.

* `@set_preferences!(pairs...)`: This allows setting multiple preferences at once as pairs.

* `@has_preference(key)`: Returns true if the preference named `key` is found, and `false` otherwise.

* `@delete_preferences!(keys...)`: Delete one or more preferences.

To illustrate the usage, we show a toy module, taken directly from this package's tests:

```julia
Expand Down
66 changes: 63 additions & 3 deletions src/Preferences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ using TOML
using Base: UUID, TOMLCache

export load_preference, @load_preference,
set_preferences!, @set_preferences!
has_preference, @has_preference,
set_preferences!, @set_preferences!,
delete_preferences!, @delete_preferences!

include("utils.jl")

Expand Down Expand Up @@ -53,6 +55,31 @@ macro load_preference(key, default = nothing)
end
end

"""
has_preference(uuid_or_module, key, default = nothing)
Return `true` if the particular preference is found, and `false` otherwise.
See the `has_preference` docstring for more details.
"""
function has_preference(uuid::UUID, key::String, default = nothing)
value = load_preference(uuid, key, nothing)
return !(value isa Nothing)
end
function has_preference(m::Module, key::String, default = nothing)
return has_preference(get_uuid(m), key, default)
end

"""
@has_preference(key)
Convenience macro to call `has_preference()` for the current package.
"""
macro has_preference(key, default = nothing)
return quote
has_preference($(esc(get_uuid(__module__))), $(esc(key)), $(esc(default)))
end
end

"""
process_sentinel_values!(prefs::Dict)
Expand Down Expand Up @@ -169,7 +196,7 @@ will be exactly what was saved here. If we wanted to re-enable inheritance from
up in the chain, we could do the same but passing `missing` first.
The `export_prefs` option determines whether the preferences being set should be stored
within `LocalPreferences.toml` or `Project.toml`.
within `LocalPreferences.toml` or `Project.toml`.
"""
function set_preferences!(u::UUID, prefs::Pair{String,<:Any}...; export_prefs=false, kwargs...)
# Find the first `Project.toml` that has this UUID as a direct dependency
Expand Down Expand Up @@ -203,7 +230,7 @@ function set_preferences!(u::UUID, prefs::Pair{String,<:Any}...; export_prefs=fa
target_toml = project_toml
if !export_prefs
target_toml = joinpath(dirname(project_toml), "LocalPreferences.toml")
end
end
return set_preferences!(target_toml, pkg_name, prefs...; kwargs...)
end
function set_preferences!(m::Module, prefs::Pair{String,<:Any}...; kwargs...)
Expand All @@ -223,4 +250,37 @@ macro set_preferences!(prefs...)
end
end

"""
delete_preferences!(uuid_or_module, prefs::String...; block_inheritance::Bool = false, export_prefs=false, force=false)
Deletes a series of preferences for the given UUID/Module, identified by the
keys passed in as `prefs`.
See the docstring for `set_preferences!`for more details.
"""
function delete_preferences!(u::UUID, pref_keys::String...; block_inheritance::Bool = false, kwargs...)
if block_inheritance
return set_preferences!(u::UUID, [k => nothing for k in pref_keys]...; kwargs...)
else
return set_preferences!(u::UUID, [k => missing for k in pref_keys]...; kwargs...)
end
end
function delete_preferences!(m::Module, pref_keys::String...; kwargs...)
return delete_preferences!(get_uuid(m), prefs...; kwargs...)
end

"""
@delete_preferences!(prefs...)
Convenience macro to call `delete_preferences!()` for the current package. Defaults to
setting `force=true`, since a package should have full control over itself, but not
so for deleting the preferences in other packages, pending private dependencies.
"""
macro delete_preferences!(prefs...)
return quote
delete_preferences!($(esc(get_uuid(__module__))), $(esc(prefs...)), force=true)
end
end

end # module Preferences
6 changes: 6 additions & 0 deletions test/UsesPreferences/src/UsesPreferences.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,11 @@ end
function get_username()
return @load_preference("username")
end
function has_username()
return @has_preference("username")
end
function delete_username()
@delete_preferences!("username")
end

end # module UsesPreferences
41 changes: 39 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ up_path = joinpath(@__DIR__, "UsesPreferences")
prefs = TOML.parsefile(local_prefs_toml)
@test haskey(prefs, "UsesPreferences")
@test prefs["UsesPreferences"]["backend"] == "CUDA"

# Now show that it forces recompilation
did_precompile(output) = occursin("Precompiling UsesPreferences [$(string(up_uuid))]", output)
cuda_test = """
Expand All @@ -92,18 +92,55 @@ up_path = joinpath(@__DIR__, "UsesPreferences")
using UsesPreferences, Test, Preferences
using Base: UUID
@test load_preference($(repr(up_uuid)), "username") === nothing
@test !UsesPreferences.has_username()
@test UsesPreferences.get_username() === nothing
UsesPreferences.set_username("giordano")
@test UsesPreferences.get_username() == "giordano"
""")

# This does not cause a recompilation, and we can also get the username back again:
username_test = """
using UsesPreferences, Test
using UsesPreferences, Test, Preferences
@test UsesPreferences.get_username() == "giordano"
"""
output = activate_and_run(up_path, username_test; env=Dict("JULIA_DEBUG" => "loading"))
@test !did_precompile(output)

_prefs_delete_username = "delete_preferences!($(repr(up_uuid)), \"username\"; block_inheritance = false)"
_delete_username = "UsesPreferences.delete_username()"
_has_username = "@test UsesPreferences.has_username()"
_doesnt_have_username = "!@test UsesPreferences.has_username()"
_set_username = "UsesPreferences.set_username(\"giordano\")"
_get_username = "@test UsesPreferences.get_username() == \"giordano\""
_doesnt_have_set_get_username = """
$(_doesnt_have_username)
$(_set_username)
$(_get_username)
"""
snippets = [
_prefs_delete_username,
_doesnt_have_set_get_username,
_has_username,
_delete_username,
_doesnt_have_set_get_username,
_has_username,
_prefs_delete_username,
_doesnt_have_set_get_username,
_has_username,
_prefs_delete_username,
_doesnt_have_set_get_username,
_has_username,
]
for snippet in snippets
code = """
using UsesPreferences, Test, Preferences
using Base: UUID
$(snippet)
"""
output = activate_and_run(up_path, username_test; env=Dict("JULIA_DEBUG" => "loading"))
@test !did_precompile(output)
end
end
end

Expand Down

2 comments on commit f52ba25

@DilumAluthge
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/27549

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.1.0 -m "<description of version>" f52ba25f1164e945052f1d2f71ca0f1d6f59ea71
git push origin v1.1.0

Please sign in to comment.