-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Flakes: provide robust access to outPath
through new meta
argument
#8908
base: master
Are you sure you want to change the base?
Flakes: provide robust access to outPath
through new meta
argument
#8908
Conversation
It seems a backward incompatible change. Could it be an on-demand attribute like nix flake registry names? |
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.
Do you want to make it backward-compatible?
Only for |
Didn't run all the test during development. My bad.
|
Since we already have some on-demand |
62c33c0
to
733537d
Compare
This way, flake frameworks have access to the flake location even when an error causes `self` not to evaluate.
... and do not add `meta` to the lock when formal is present, like we do for `self`.
733537d
to
e3b90dd
Compare
} | ||
} | ||
|
||
static RegisterPrimOp primop_functionStrict({ |
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 should be functionHasFormals
or whatever. It is not immediately obvious to the casual user what a “strict function” is supposed to be (and it is a bit ambiguous as well, I'd say). A function with formals is immediately syntactically obvious, so there is no confusion what this builtin would check for.
Additionally, not all functions that are strict in their argument have formals, as the documentation also admits, e.g.:
foo: builtins.seq foo (/* body … */)
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 couldn't care less about the syntax of a function, which is why I've named these primops after the semantics.
By calling it has formals we can't go back and add a syntax to make functions with formals lazy. What matters is that the function definition is trivially strict, so that's what the name reflects. Should it be functionTriviallyStrict
?
If this function is too contentious, I might remove it because it turned out that this one wasn't necessary for the use case I'm trying to solve. I think it's good to have, but low impact, so I don't want to waste time on it.
not all functions that are strict in their argument have formals
This is for improving error messages in a few cases, and nothing more.
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.
Well the semantics are fundamentally not introspectable, so the naming is very deceptive.
we can't go back and add a syntax to make functions with formals lazy
It wouldn't really matter, the introspection would still do its job as advertised—there would be a breaking change in the semantics of the language, but that would be a problem regardless of the existence of that builtin.
.fun = prim_functionStrict, | ||
}); | ||
|
||
static void prim_functionOpen(EvalState & state, const PosIdx pos, Value * * args, Value & v) |
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.
Open is new terminology that has not appeared so far, as far as I am aware. I'd stick to something with ellipsis which is also terminology used by builtins.toXML
.
.fun = prim_functionOpen, | ||
}); | ||
|
||
static void prim_functionBindsAllAttrs(EvalState & state, const PosIdx pos, Value * * args, Value & v) |
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.
What is this needed for? I don't really see an use case for this right away.
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.
It's used in this PR to report a warning when a flake's outputs
function is invoked. This PR adds an attribute, which causes somewhat of a problem when the function is of the form
args@{ self }:
The lack of ellipsis means that we'd have to remove the attribute for compatibility, while args@
exposes that workaround, which would be a source of confusion. Detecting this situation and reporting a warning means that we get a reasonable upgrade story for this feature.
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.
Why not just check for an ellipsis (functionOpen)? Whether the user also binds the whole attribute set, is not really interesting for 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.
If it has ellipsis, but no binding for the whole attrset, there's no conflict and the backcompat logic can comply with the old interface and it doesn't have to warn about anything.
I don't want it to warn about something that isn't a problem, because too many warnings lead people to ignore them.
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.
Right, but why not just warn if it lacks an ellipsis. The full attribute set binding should be irrelevant.
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.
The full attribute set binding should be irrelevant.
The ambiguity of #8908 (comment) does not occur if the full attrset binding is missing. So to keep warnings to a minimum, I want to know whether the full attrset binding exists or not.
I can agree that it should but unfortunately it would require me to warn in cases where it doesn't matter.
These are relevant for determining compatibility properties of functions. - Can I add an attribute? Is it open for extension? - If I omit an attribute that I'd be expected to add, does that lead to a conflict? Such a conflict arises when the function is closed and also binds all attributes using `@` syntax. It also answers a related question: is the function defined using the strict syntax? This is useful for explaining the misconception that changing a plain lambda to a strict lambda is a no-op refactor. The fact that it makes the function strict is overlooked and the error message, infinite recursion sends users into panic mode. With the new functionStrict primop we can write functions that catch the mistake before we enter it, which could be very helpful.
e3b90dd
to
7b81118
Compare
Inputs is not quite an appropriate name because of `self` and `meta`, which aren't inputs.
This doesn't suggest that two is the total count, and I think it's a nice way to phrase it, because the functions are related.
... and fix an incorrect use of `grep -v`
7b81118
to
72675eb
Compare
I'm considering the following simplification.
I believe this still covers the needs for the warning, and it simplifies the code a little. Advantages
Disadvantages
I'm leaning towards doing it this way. |
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2023-09-18-nix-team-meeting-minutes-87/33194/1 |
Other meta attributes could include the raw top level attributes, such as |
outPath
through new meta
argument.outPath
through new meta
argument
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2024-11-27-nix-team-meeting-minutes-198/56691/1 |
Motivation
inputs.self
declaration invalid (at least until we have self fetching configuration)When reporting an error, it is beneficial to include the location of the flake that causes it. For this, a flake framework needs to access
outPath
, butoutPath
is only available inself
.This PR adds a new
outputs
argument besides the inputs. We already hadself
; now we also havemeta
.I've chosen
meta
for a couple of reasons:outputs
function. It would be nice to be able to avoid this problem in the future, andmeta
provides such an extension point.flake.outPath
is hard. I'd like to have a better name for it, such asflakeDir
, but creates a more questionable scope. Even if we find a nice and consistent name for it, we'll want to put it inmeta
anyway.The problem can be illustrated by the new test:
The ability to refer to
args.meta.extraAttributes.outPath
is crucial for conveying what's the origin of the error, especially when the error occurs within some input.An flake name would also be very helpful, and it could be added to
meta
once we agree that a flake can declare a name for itself (in a "non-binding" way like we have for derivations). Again something that I'd like to scope out because it requries more discussion that's not quite necessary for making substantial progress on the problem.Context
self
assigned to option hercules-ci/flake-parts#185mapAttrs
, errors will tend to have locations from the framework rather than the flake that uses it. They should also add the location of their calling flake to their error messages where possible.Checklist for maintainers
Maintainers: tick if completed or explain if not relevant
tests/**.sh
src/*/tests
tests/nixos/*
Priorities
Add 👍 to pull requests you find important.