Makie.jl icon indicating copy to clipboard operation
Makie.jl copied to clipboard

Avoid `activate!`ing at `__init__` time

Open ericphanson opened this issue 2 years ago • 3 comments

This makes it hard to load a backend "in the background" in a way that doesn't effect the end-user.

I'm not really sure how to do this and preserve the "convenience activation" feature where a user doesn't have to manually activate!.

My use case

  • We have a library https://github.com/beacon-biosignals/Lighthouse.jl which uses Makie to generate figures. It doesn’t save them, so it doesn’t depend on any backends, but it does pass them to a logging interface (log_plot!).
  • Then we have a logging package (not yet open source) which wants to implement this logging interface and save these plots to disk and send them off to their destination.
  • So I think this logging package should depend on CairoMakie, so that it can save Makie figures to png’s. However, I don’t think loading a logging package should affect the overall global state, and I don’t want the user’s active backend to depend on whether or not they just loaded this logging package.

ericphanson avatar May 20 '22 10:05 ericphanson

a way that doesn't effect the end-user

This is actually more tricky than I thought, I didn't come up with a solution, yet.

If we don't activate a backend at all at __init__, people will always have to manually do that before plotting, which is undesirable of course. So we need to do something that will cause a backend for which we call using to be used for output.

But let's consider some scenarios, first for the case where we activate any backend at using time (how it is now):

  • user uses A, package uses B in backgroud -> B is active ❌
  • package uses A in background, user uses B -> B is active ✅

Now let's say we don't activate directly, but record which backend was loaded and just use the first/last loaded one unless one is specifically activated. We have the same problem that a package loading a backend before/after the user will change which backend outputs, unless the user calls activate!.

What I think is what we need, is for a backend to be loaded while actively telling it not to activate itself. An automatic mechanism is unlikely because even if we could discern a using at top-level from a using in a package, we wouldn't know if the using in a package is completely expected and wanted by the user because they expect another package to reexport Makie.

The only way I currently see is to store a list of backends in their order of activation, and export a deactivate!() function which just removes that backend from the list. Calling that from a background package would then remove it from the list. Although this should not deactivate the backend if the user had already loaded it anyway. And it feels kind of unclean.

Any other ideas?

jkrumbiegel avatar Jul 07 '22 10:07 jkrumbiegel

One idea is to switch the activate system to Preferences.jl. This records the preference in a toml in the project. So it could be something like:

  • loading a backend sets the preference if it isn’t set yet
  • if it is set, loading a backend doesn’t do anything
  • manually activating sets the preference

This isn’t the exact same semantics as currently, but it might be better: it exposes the state to the user (instead of a global in the module it’s written to a file they can see and edit), and it lasts between sessions.

I guess then you have the issue if e.g. the glmakie preference is set but glmakie isn’t loaded while say CairoMakie is; do you error when saving or do you change the preference to the one that’s loaded. Not sure..

ericphanson avatar Jul 07 '22 10:07 ericphanson

Yes that one also doesn't really solve it.. I have to think more about it

jkrumbiegel avatar Jul 08 '22 16:07 jkrumbiegel