[ANN] PreferenceTools.jl - interactive preference setting

PreferenceTools.jl

I made a little package to make it easier to set and get preferences (in the sense of Preferences.jl) interactively. It hooks into the Pkg REPL so you can do this:

pkg> preference add Plots default_backend=unicodeplots

pkg> preference add Cthulhu enable_highlighter=true type_annotations=true

pkg> preference add SnoopPrecompile skip_precompile+=DataFrames

pkg> preference status
Cthulhu
  enable_highlighter: true
  type_annotations: true
Plots
  default_backend: "unicodeplots"
SnoopPrecompile
  skip_precompile: ["DataFrames"]

Why use this?

  • It provides a single consistent interface for modifying preferences. Many packages have functions to set their preferences, but with varying interfaces and levels of functionality. Package authors could instead instruct their users to set preferences with this package.
  • The commands (add, remove, status) are consistent with the rest of the Pkg REPL so easy to remember.
  • It allows you to set preferences before first loading the package - which may be important to avoid downloading a large unwanted default binary, or to avoid unnecessary precompilation. You can only do this with Preferences.jl if you know the UUID of the package.
  • You can conveniently set global preferences with the -g flag, and export them with the -x flag.
  • Fewer keystrokes - installed packages and existing preference names can be tab-completed.
  • List-valued preferences can be modified with += and -=, which is particularly useful for SnoopPrecompile.skip_precompile.

Why not use it?

  • Preferences are not validated at all - this does not check that the package name exists, or that a given preference name or value is valid for the package.
  • Packages which consume preferences should still use Preferences.jl directly. This package is for interactive use.

Personally, I’m going to migrate some of my own packages (PythonCall, CondaPkg, Bokeh) to use preferences, instructing users to set preferences with this package.

24 Likes

Have you considered a PR to Preferences.jl instead of having a separate package? This seems like the sort of thing that we want standardized in the long run (at least for simple set/get operations).

9 Likes

Sure, there are certainly some things that could be immediately “upstreamed” to Preferences.jl, like the ability to specify the package by name.

The REPL mode is sufficiently new and different though (and relies on undocumented internals) that I was expecting some push-back on that. Having a separate package makes it easier to iterate and get to an acceptable implementation. It’s also possible the REPL mode might make the package load time a lot worse for people not using that functionality - though I imagine it can be avoided by only enabling it in interactive sessions.

1 Like

PreferencesTools.jl has extra dependencies besides Preferences.jl itself, which I don’t think would be accepted in Preferences.jl.

Does it? Only for testing, no? (Markdown and Pkg are stdlibs.)

I was referring to them. I don’t think anyone will be happy about loading Pkg in all jlls :slightly_smiling_face: With [AutoBuild] Do not add `Pkg` as dependency if we require Julia v1.6 by giordano · Pull Request #1261 · JuliaPackaging/BinaryBuilder.jl · GitHub I removed it completely from the environment, even if it wasn’t actually loaded most of the time. Pkg is large.

1 Like

Why does Preferences.jl have a comparatively long load time, actually? It indirectly affects the load time of SnoopPrecompile, which I imagine will be used by a lot of packages in the future. Preferences.jl should have a lot of invalidations, etc., right?

julia> @time_imports import Pkg, SnoopPrecompile
     42.5 ms  Preferences
      0.6 ms  SnoopPrecompile

If the problem is adding Pkg as a dependency for BinaryBuilder.jl, maybe we can upstream all of this to Preferences.jl, then spin off a PreferencesLite.jl for BinaryBuilder?

No, it’s not. The problem is that people don’t want Pkg as a dependency needlessly.

I don’t think this helps with the problem stated above, it just aggravates it.

Right, so what I’m saying is you can carve out the parts of Preferences.jl and PreferenceTools.jl that don’t require Pkg.jl and put them in a PreferencesCore.jl, which people who don’t want to use Pkg can use instead.

Mostly I’m trying to make this easy on the user end, by having one package for normal users with a simple name (Preferences.jl).

Separately, for the kinds of people who care a lot about minimizing dependencies, we can have a package that includes any preference-related features that don’t require Pkg.