-
-
Notifications
You must be signed in to change notification settings - Fork 14.3k
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
[WIP] lib.sources.predicateFilter: init #221361
Conversation
A filter function that works on subpaths instead of absolute paths, also works for lazy paths
95faec4
to
81dae56
Compare
lib/sources.nix
Outdated
@@ -270,6 +270,19 @@ let | |||
outPath = builtins.path { inherit filter name; path = origSrc; }; | |||
}; | |||
|
|||
predicateFilter = { name ? "source", src, predicate }: |
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 don't think this is a move in the right direction. By repeating each feature in the argument list, we're creating the square of the simpler api proposed in
I strongly suggest going with the two-argument filter
and setName
functions. They do not need extensibility because they have a single, well-defined responsibility. Any new responsibilities can be covered by new functions instead. Let's keep it simple.
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.
Zooming back out it's actually not clear at all that a relative path would be a better predicate input than a path value. It breaks the following:
filter (path: type: path != ./src/README.md) ./src
and it creates ambiguity about what this means
filter (path: type: path != "./src/README.md") ./src
and it highly complicates the transition from
filter f ./.
to
filter f (extend ../globals.mk ./.)
The amazing benefit of path expressions is that you don't need to think about any evaluation whatsoever to determine which file is referenced.
By using relative path strings you lose this incredibly powerful rule.
lib/sources.nix
Outdated
isFiltered = src ? _isLibCleanSourceWith; | ||
origSrc = if isFiltered then src.origSrc else src; |
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 duplicates toSourceAttributes
. Please reuse that.
lib/sources.nix
Outdated
filter = absolutePath: type: | ||
assert lib.isString absolutePath; | ||
let subpath = removeBase (lib.substring 1 (-1) absolutePath); | ||
in predicate { inherit type subpath; }; |
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 alternate implementation of the filter function blocks composability with the other functions. Both styles of filter function can be supported across the api, by extending the normalized representation that is returned by toSourceAttributes
. Instead of the current filter
attribute, it could have three attributes: absFilter
, relFilter
and preferRelFilter
.
The main consideration here is that
The function introduced here fixes most of those problems (the last one only partially) by using subpath strings for filtering instead, establishing a cleaner foundation to base source filtering on. The main reason for this is really the lazy trees mess, which is entirely abstracted away with this. But also note that I don't think this interface needs to be directly exposed to the user in most cases (maybe all?), instead a source combinators API (supporting path expressions) should be useful enough to (almost?) never have to write a custom filter function or subpaths. |
This doesn't actually work btw, you'd need |
I'm not arguing for absolute paths strings. I'm arguing for path values. Nonetheless, functions that work with either representation can be made compatible, as long as they operate on a shared representation that is complete enough. That's not to say that we shouldn't design primarily for path values though. Path values are the most useful representation, so if any compromises would need to be made, let's make them in favor of path values.
This should be replaced by a new primop, e.g.
This can stick around for compatibility, but I agree that it is not good.
"Let's use a low level interface and implement it ourselves" feels like a safe choice because you have "full control" or you can "implement it exactly the way you like", but to buy into that, you need to ignore that path values are already a very good abstraction, and path values already exist and users must interact with them regardless of the choices we make in this library.
I will keep reminding Eelco that breaking path semantics is not ok, and we can all make sure that lazy trees keeps offering the primitives we need for the filtering library.
It is though. If it exists, people are going to use it, and it when we have three styles of interface, people will hate all of them, because they don't know what to expect where. Many users won't touch source filters for months on end, so don't count on anyone memorizing a map of the source filtering landscape. It should be simple and consistent.
That is the goal, but I wouldn't predicate the design of the library on users using it in the ideal manner, if at all possible.
Works just fine when |
Yeah, I guess I shouldn't have implied that it will be exposed. So far I am convinced that we don't actually need to expose it. However, you got me thinking..
Are you sure that in this case they actually are? They do have disadvantages compared to subpaths if you want to use them for source filtering:
If I get what you mean, no it doesn't. All the filtering functions (also TIL there's a new one in lazy trees,
At least that's not what any of the current filtering functions do, none of them work if you just try to do |
I am just realizing that lazy tree's
|
4e301df
to
e12b54c
Compare
Functions for comparing paths
I implemented one path-based and one subpath-based
Notably with such a |
But I'm just realizing that a |
All source filtering primops have been wrapped so far, and the focus of that branch is on the fetchers, not necessarily on source filtering. I wouldn't count too much an that function being a deeply thought out design.
Checking Suppose you have a filter f that rejects let
build = f: dir: doIt {
doc = filter f (dir + "/doc");
src = filter f (dir + "/src");
};
in
{
pkg = build (path: type: true) ./pkg;
pkgWithoutDoc = build (path: type: path != ./doc) ./pkg;
}
Conversely, if you change the root of the source, perhaps because you need to include something from a parent directory, you have to update the subpath strings.
This is unnecessary IFD anyway. Good riddance.
|
Interesting, I haven't considered that. At least for manual filter writing this is more cumbersome, since e.g. just
Good point, I'd say these two use-cases are needed about equally often too.
I wasn't thinking of IFD, but instead of a
At least #221204 doesn't prevent that case yet, but yes it probably shouldn't do that. I'll think about the interface more with the points you made. |
Closing as this won't be necessary, see #222981 |
Description of changes
WIP! This function may not be needed in the end because a higher-level abstraction might be better suited.
This PR adds
lib.sources.predicateFilter
, which is likebuiltins.{path,filter}
orlib.sources.cleanSourceWith
, except that it calls the filtering function with relative paths, specifically subpaths, as introduced in #205190. It also improves on the interface a bit by not using ordered arguments. The usefullib.path.subpath.hasPrefix
function is also introduced here, relating to #210423. This is more generally related to #210426.Most notably, as is also the case with all the
lib.path
functions, this function works exactly the same way with the lazy trees PR, despite it causing breakages for other source filters.This work is sponsored by Antithesis ✨
Things done