-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document "exported" preferences #48
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,22 +11,35 @@ | |
|
||
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 in Julia v1.6+. | ||
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. | ||
If a preference is "exported", it is instead stored within the `(Julia)Project.toml`. | ||
The intention is to allow shared projects to contain shared preferences, while allowing for users themselves to override those preferences with their own settings in the `LocalPreferences.toml` file, which should be `.gitignore`d as the name implies. | ||
|
||
Note that the package can be installed on Julia v1.0+ but is only functional on Julia v1.6+. | ||
|
||
## Project-specific vs package-wide preferences | ||
|
||
Preferences are stored as TOML dictionaries and are, by default, stored within a `(Julia)LocalPreferences.toml` file next to the currently-active project. This results in *project-specific* | ||
preferences, meaning that different projects making use of the same package can set up | ||
different, non-conflicting preferences. | ||
|
||
Preferences can be set with depot-wide defaults; if package `Foo` is installed within your global environment and it has preferences set, these preferences will apply as long as your global environment is part of your [`LOAD_PATH`](https://docs.julialang.org/en/v1/manual/code-loading/#Environment-stacks). | ||
Preferences in environments higher up in the environment stack get overridden by the more proximal entries in the load path, ending with the currently active project. | ||
This allows depot-wide preference defaults to exist, with active projects able to merge or even completely overwrite these inherited preferences. | ||
See the docstring for `set_preferences!()` for the full details of how to set preferences to allow or disallow merging. | ||
|
||
In contrast, *package-wide* preferences are stored within within the package's own `(Julia)Project.toml` file. Such preferences apply to all users of the package, regardless of the active project. | ||
|
||
You can control which kind of preference you create; this is discussed in the API subsections below. | ||
|
||
## Run-time vs compile-time preferences | ||
|
||
Preferences that are accessed during compilation are automatically marked as compile-time preferences, and any change recorded to these preferences will cause the Julia compiler to recompile any cached precompilation `.ji` files for that module. | ||
This allows preferences to be used to influence code generation. | ||
When your package sets a compile-time preference, it is usually best to suggest to the user that they should restart Julia, to allow recompilation to occur. | ||
|
||
Note that the package can be installed on Julia v1.0+ but is only functional on Julia v1.6+. | ||
If you call `load_preference` (or its macro variant `@load_preference`) from "top-level" in the package, | ||
this is a compile-time preference. Otherwise (e.g., if it is "buried" inside a function, and that | ||
function doesn't get executed at top-level), it is a run-time preference. See examples in the first API section below. | ||
|
||
## API | ||
## API: Project-specific preferences | ||
|
||
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!()`. | ||
|
||
|
@@ -70,6 +83,7 @@ end | |
|
||
|
||
# A non-compiletime preference | ||
# These can change dynamically, and no Julia restart is needed. | ||
function set_username(username::String) | ||
@set_preferences!("username" => username) | ||
end | ||
|
@@ -80,6 +94,41 @@ end | |
end # module UsesPreferences | ||
``` | ||
|
||
With the macros, all preferences are project-specific. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it would be better to figure out how to pass kwargs to a macro, so we can have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that would make sense. A related issue: currently you have to read the docstring for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I don't understand |
||
|
||
## API: package-wide preferences | ||
|
||
To set preferences for *all* users of a package (across many different projects), use the functional form | ||
|
||
```julia | ||
set_preferences!(module, prefs...; export_prefs=true) | ||
``` | ||
|
||
To use this approach, the example above might become | ||
|
||
```julia | ||
module AlsoUsesPreferences | ||
|
||
function set_backend(new_backend::String) | ||
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 | ||
# Export it for all users of the package (export_prefs=true): | ||
set_preferences!(@__MODULE__, "backend" => new_backend; export_prefs=true) | ||
@info("New backend set; restart your Julia session for this change to take effect!") | ||
end | ||
|
||
⋮ | ||
|
||
end | ||
``` | ||
|
||
You can use the explicit module name, `AlsoUsesPreferences`, as the first argument to `set_preferences!`, but consider using `@__MODULE__` instead, as it continues to work even if you decide to rename your package. | ||
|
||
You can set preferences for another, unloaded package, using the package `UUID` in place of the module. | ||
|
||
## Conditional Loading | ||
|
||
To use `Preferences` with Julia 1.6 and later but falling back to a | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't exactly true; a preference can still be loaded from within a function and used at compile time if that function is invoked during precompilation time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"and that function doesn't get executed at top-level"? But obviously it's not clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, yeah. I'm honestly not sure it matters much to draw a distinction between compile-time and non-compile-time preferences; perhaps we should just have a note that says "if you use a preference at compile time, you will have to restart Julia before the changes will be seen" and leave it at that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, perhaps we could instead have
set_preferences()
spit out a@warn()
when it notices a compile-time preference has been changed?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that idea!
We still might need to discuss this issue somewhere, otherwise people might be mystified about why it sometimes requires a restart and why it sometimes doesn't. (Might feel like a bug when it doesn't warn.)