jwiegley / use-package

A use-package declaration for simplifying your .emacs
https://jwiegley.github.io/use-package
GNU General Public License v3.0
4.41k stars 260 forks source link

A :maybe-ensure keyword #608

Open purcell opened 6 years ago

purcell commented 6 years ago

I don't currently use use-package myself, but I was playing with it a while ago and wanted certain use-package clauses to degrade gracefully if the packages were uninstallable on the current host Emacs. I came up with the following, which someone might find useful, so I thought I'd post it here in case it would be a generally useful addition.

  (defun use-package-normalize/:maybe-ensure (name keyword args)
    (use-package-normalize/:ensure name keyword args))

  (defun use-package-handler/:maybe-ensure (name keyword ensure rest state)
    (condition-case err
    (use-package-handler/:ensure name keyword ensure rest state)
      (error
       (message "Couldn't install optional package: %S" err)
       nil)))

  (add-to-list 'use-package-keywords :maybe-ensure))
purcell commented 6 years ago

P.S. Perhaps :ensure-noerror would be a better name...

nickserv commented 6 years ago

What could cause a package to be uninstallable? Would it be a platform specific or repository specific issue or something specific to a package not working properly?

Also what's the current behavior, would use-package stop loading the rest of the use-package forms in this case?

purcell commented 6 years ago

What could cause a package to be uninstallable? Would it be a platform specific or repository specific issue or something specific to a package not working properly?

If I try to :ensure a package which depends on Emacs 25.2 in Emacs 25.1, for example.

In my personal config, which a bunch of crazy people seem to also use for some reason, I loosely support a wide range of Emacsen, but omit certain functionality if the host Emacs is not sufficiently recent.

Also what's the current behavior, would use-package stop loading the rest of the use-package forms in this case?

Yes, I believe so, but I haven't tested recently. When package.el fails to install a package due to unresolved dependencies, I believe it looks the same to the startup elisp as if the machine were offline. IIRC package.el can determine that a package will be uninstallable by looking at the local package archive info, so there shouldn't be constant network lookups at start-up time if there are use-package forms containing unsatisfiable :maybe-ensure flags.

nickserv commented 6 years ago

Thanks for the explanation. I would be in favor of making :ensure degrade gracefully when the Emacs version doesn't work (just like a falsey :if or a missing dependency in a normal use-package form), unless @jwiegley wants to keep this separate.

purcell commented 6 years ago

Yes, that might also make sense.

jwiegley commented 6 years ago

What do you guys want the default behavior to be? I don't use :ensure.

phillord commented 6 years ago

Funnily enough I was just thinking of this today. For my money, I would like :ensure to, well ensure, but if it fails to work equivalent to :disabled t.

The scenario I hit most often is this. I update my .emacs using unison or other file sync tool. Then I hit the road, and try to run Emacs. During the file sync, I've pulled in something that adds an new ensure. Now I can't start Emacs cleanly, because I am offline. Instead I think use-package should issue warnings about failed ensures both otherwise leave a usable Emacs (minus the packages that It can't install).

purcell commented 6 years ago

The choice of default behaviour hinges on the question of whether a use-package user should be able to (or should have to) specify ahead of time whether failing to install any given package will break their config. This seems quite at odds with the basic selling point of use-package.

So probably the default should indeed be to have :ensure soft-fail. The level of outrage it expresses is a separate question, then. If one uses this mechanism to make an emacs config degrade gracefully on older Emacsen in which certain packages can't be installed, then outright warn calls would be overly intrusive. However, in @phillord's use case they're probably the best choice.

So how about a new defcustom like use-package-ensure-failed-hook (or -handler), which would name a function to be invoked when a package cannot be ensured? The default would be to call warn, but users could change this to produce either a hard error, or a quiet message? (Sophisticated users could presumably then let-bind this over certain use-package forms if they really wanted to.)

phillord commented 6 years ago

I am actually in two minds about this. I am definately in favour of a hard-fail -- this is surely much better when you are getting your config sorted. A nice big breakage is just what you need -- a puny message hidden in the spam of a normal start up is not good. Otherwise, you think everything is fine then spend ages trying to work out what is happening when functionality that you think should be there is not.

Of course, I am also totally in favour of a soft-break when I am offline. In the ideal world, use-package should use esp.el to work out which.

More sanely, I think a simple boolean would be enough. Looking though the code base, I realise that even this is not 100% necessary, since use-package already has a "what function should I call to do the ensure" variable. Does use-package not have any general variable saying whether things should be fail-fast or soft-fail? A single point of configuration probably makes more sense than 20 variables for each keyword.

purcell commented 6 years ago

A nice big breakage is just what you need

That's why I suggested something based on warn as the default: it pops up a window that's hard to ignore, while stopping short of an outright error.

Looking though the code base, I realise that even this is not 100% necessary, since use-package already has a "what function should I call to do the ensure" variable.

Ah yes: use-package-ensure-function! In that case, given the direction that this has been going, perhaps what we need is a use-package-ensure-elpa-noerror as an alternative value for that defcustom. Though it's not obvious to me if returning nil from that would be sufficient to skip the entire use-package form.

phillord commented 6 years ago

As it stands, no it wouldn't. One thing I did notice is that the require form that is generated is surrounded by a condition-case-unless-debug. Pushing the ensure call into this would solve the problem.