-
-
Notifications
You must be signed in to change notification settings - Fork 14.6k
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
Improve callPackage error message! #79877
Conversation
c11260b
to
27de341
Compare
errorForArg = arg: "Function ${prettyLocation}" | ||
+ "called without required argument \"${arg}\"${prettySuggestions (getSuggestions arg)}"; | ||
error = lib.concatMapStringsSep "\n" errorForArg missingArgs; | ||
in if missingArgs == [] then makeOverridable f allArgs else throw error; |
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 looks like it won't work for invocations that only become valid after overriding. I hope I'm wrong.
Also this is demanding missingArgs
on all callPackage
invocations. What's the performance impact?
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 guess you mean something like this:
(lib.callPackageWith {} ({ foo }: foo) {}).override { foo = 10; }
However this also throws an error even before this change.
Yeah the performance needs to be investigated, I think it's going to be minor though
As much as I want this feature, I doubt that it should be implemented in Nixpkgs rather than Nix itself. |
@roberth Unless What could be implemented in Nix however is the same thing for attribute access like |
I wonder what it would take to implement this in Nix itself, to get this goodness everywhere! edit: GitHub hadn't loaded the comments made since I opened this page. |
Yeah so I'm pretty sure now. This only works due to the fact that Edit: Though really, there's not much benefit to implementing this in Nix with something like |
After some more tinkering I was able to come up with this change: diff --git a/lib/customisation.nix b/lib/customisation.nix
index ac234e3b8c6..9944d3330f2 100644
--- a/lib/customisation.nix
+++ b/lib/customisation.nix
@@ -118,7 +118,10 @@ rec {
let
f = if lib.isFunction fn then fn else import fn;
auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
- in makeOverridable f (auto // args);
+ allPotentialArgs = autoArgs // args;
+ filled = lib.mapAttrs (name: value: allPotentialArgs.${name})
+ (lib.filterAttrs (name: value: ! value) (lib.functionArgs f));
+ in makeOverridable f (filled // auto // args);
/* Like callPackage, but for a function that returns an attribute This results in this error now:
So this change makes it look up all arguments in the available packages, resulting in an attribute access error instead of a function call error for missing arguments. With this, implementing such a feature for attribute access in Nix directly (as in So, this could be implemented in Nix directly, either by:
If losing error location (without |
The function location can be recovered by allowing functions as the first argument to Alternatively, The readability concern is minor if the big let binding is moved out into a separate function. A possible benefit of generating suggestions in Nix is you can more easily add domain-specific logic. For example giving special treatment to common errors for which Levenshtein distance doesn't work too well, like adding/removing a (If we go with a Nix implementation) Did you try canonicalizing the names before running Levenshtein? You could
|
@roberth I like your idea of being able to have more domain-specific behavior with an implementation in Nix code. I think doing those suggestions here would decrease code readability a bit too much for now, and the current approach has some leeway for things like So, I'm kind of convinced this PR is a good idea because:
Imo this can be merged after performance is confirmed to not be dramatically worse (which I doubt). And after some tests are added for the levenshtein functions |
The time to evaluate
So I think it's safe to say that performance is influenced minimally by this. Edit: Updated stats to discard the first sample for warmup |
I like this PR. Given the recent evaluation memory wall we hit (and are still having trouble with) I am -1 on this PR merging right now. I would like to see that stabilize before adding this. I know it may not make it worse, but I don't want to tempt fate today. |
It's a nice feature, but Nix is not a general purpose language so this definitely should not be implemented in Nix code. |
@infinisil The evaluation time of your benchmark may be dominated by time spent in the module system. Benchmarking Nixpkgs by itself should help to make the results more significant, considering that standard error estimate is currently rather high. |
@edolstra I guess you're referring to the levenshtein distance being implemented in nix code? That's the only thing that's "general-purpose" here. Yeah I guess a |
@infinisil Yes, exactly. Nix isn't really suited for implementing anything that has the word "algorithm" in it :-) E.g. Nix doesn't have a type system, the evaluator is not very fast, etc. I don't think the solution is to have |
@edolstra As explained above already, this either has the disadvantage of: Losing error context if using #79877 (comment) or significant complexity in Nix builtins using #79877 (comment). If you know of a way to get an error message without losing context and without too much complexity, let me know, because I don't see it. And as such, I still think this PR is the best way to implement this. |
Isn't
Is there any reason to use reflection and
Both of these seem solvable. What we get in returns is better error messages as implemented in Nix. We want to have it there for the general case of attrset invocations anyway. Furthermore, we simplify Nixpkgs by getting rid of needless use of reflection and we get the same behavior as with the module system, which also requires Also we can replace this entire nix pill https://nixos.org/nixos/nix-pills/callpackage-design-pattern.html by a footnote about how laziness is working really well. No need to waste people's time on legacy. Please correct me if I'm wrong :) EDIT (Nov 13): I'll abuse this comment to keep notes. First of all thanks for clarifying the relevance of |
@roberth For one, I like the idea of redesigning how packages are declared in nixpkgs, the current way seems clumsy in many ways (multiple dependency listings, what's up with optional dependencies, what's up with configuration, why |
@infinisil I agree, except for the long ways away. It depends on the migration path we choose, but either way it's going to be a 'mechanical' process; simple and boring, which is good. |
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/fetchfromgithub-in-configuration-nix/5927/10 |
I think a few people have misunderstood @edolstra's position here. He doesn't want to implement this language in code evaluated by the evaluator -- ie: he doesn't want this solution to be done with the Nix Expression Language. Instead, he would like to see the solution implemented as a patch to the Nix Evaluator's error messages, in the NixOS/nix repo. |
If we're on the topic of simplifying package expressions, we should ask whether packages should be functions at all. The main reason why they are functions is because you don't want to define all packages in one giant file. But things become much simpler if you do put everything in a giant file, see for example
where
i.e. it doesn't take any arguments but just expects to be included in a lexical environment containing This style does break Another possibility is Bazel-style dependencies where packages themselves specify where to get their dependencies, so something like
but this changes semantics a lot. |
This style does break `.override` but arguably that's a good thing.
We even use overrides in Nixpkgs proper, and this _is_ useful functionality.
|
I propose we merge then. |
I don't think it's a particularly good solution, but it solves a problem. If it causes any issues, it can be reverted. For readability, you could factor the error checking and throw into a helper function as much as possible. That should leave a concise implementation of what |
I still don't think that the Nixpkgs standard library is the right place for functions like |
Could we name it |
Just move it to a let binding inside the place it’s used instead of
exposing it from lib?
…On Fri, Oct 9, 2020 at 12:10 PM Jan Tojnar ***@***.***> wrote:
Could we name it _internalLevenshtein, or require the user to call it as _internalLevenshtein
{__acceptThisFunctionIsInternalAndCanBeRemovedAtAnyTime = true; } a b to
rid us of such support requirements?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#79877 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAYB5ZUBXBLCABR2NRA6FFDSJ3ORRANCNFSM4KTNQRHQ>
.
|
Private definitions are harder to run tests for. |
To be exact, the |
But also, I don't think there's a problem with having a generic |
e877834
to
a8afec1
Compare
I wouldn't mind this being merged, though I might continue my attempt at implementing this in Nix directly soon, which then would make this PR redundant. |
a8afec1
to
52f92aa
Compare
52f92aa
to
3a7b48d
Compare
So let’s get rid of the generic levenshtein function in this PR and only
add the specialized version for now, it seems like this was the only
objection left.
…On Sat, Oct 10, 2020 at 12:17 AM Silvan Mosberger ***@***.***> wrote:
I wouldn't mind this being merged, though I might continue my attempt at
implementing this in Nix directly soon, which then would make this PR
redundant.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#79877 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAYB5ZQVTCVKXVDZALGQGILSJ6DV5ANCNFSM4KTNQRHQ>
.
|
I marked this as stale due to inactivity. → More info |
I’d still like to get this merged. I am okay with the nix levenshtein implementation. |
Adds some functions related to string similarity: - lib.strings.commonPrefixLength - lib.strings.commonSuffixLength - lib.strings.levenshtein - lib.strings.levenshteinAtMost
This uses the levenshtein distance to look through all possible arguments to find ones that are close to what was requested: error: Function in /home/infinisil/src/nixpkgs/pkgs/tools/text/ripgrep/default.nix called without required argument "fetchFromGithub", did you mean "fetchFromGitHub" or "fetchFromGitLab"? With NixOS/nix#3468 (in current nixUnstable) the error message becomes even better, adding line location info
3a7b48d
to
71120e0
Compare
I took the liberty of rebasing on current master (only the tests had a merge conflict). |
It still works for me:
I would merge within a few days. |
Motivation for this change
Oftentimes arguments in package declarations are misspelled, leading to errors like
which doesn't give any indication as to what the correct attribute is. Well with this change it does! The error now is
Which imo, is pretty damn fancy!
The implementation searches through all attributes in
pkgs
for one with a Levenshtein distance of 2 or less, catching most misspellings. Had to jump through some hoops to make this fast though.Ping @alyssais @capsensitive @worldofpeace @Synthetica9 @roberth @Profpatsch
Better alternative to changes like #79854
Things done