2023-11-20 03:02:11 +01:00
/* O p e r a t i o n s o n a t t r i b u t e s e t s . */
2017-07-29 02:05:35 +02:00
{ lib }:
2009-02-09 17:51:03 +01:00
2017-02-09 02:52:13 +01:00
let
2024-02-09 05:33:27 +01:00
inherit ( builtins ) head length ;
inherit ( lib . trivial ) mergeAttrs warn ;
2021-12-02 18:12:51 +01:00
inherit ( lib . strings ) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName ;
2024-02-09 05:33:27 +01:00
inherit ( lib . lists ) foldr foldl' concatMap elemAt all partition groupBy take foldl ;
2017-02-09 02:52:13 +01:00
in
2009-02-09 17:51:03 +01:00
rec {
2023-04-19 21:27:35 +02:00
inherit ( builtins ) attrNames listToAttrs hasAttr isAttrs getAttr removeAttrs ;
2009-02-09 17:51:03 +01:00
2016-02-28 22:49:42 +01:00
/* R e t u r n a n a t t r i b u t e f r o m n e s t e d a t t r i b u t e s e t s .
2023-12-13 18:38:41 +01:00
Nix has an [ attribute selection operator ` . or ` ] ( https://nixos.org/manual/nix/stable/language/operators #attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
` ` ` nix
( x . a . b or 6 ) == attrByPath [ " a " " b " ] 6 x
# and
( x . ${ f p } . " e x a m p l e . c o m " or 6 ) == attrByPath [ ( f p ) " e x a m p l e . c o m " ] 6 x
` ` `
2016-02-28 22:49:42 +01:00
Example :
x = { a = { b = 3 ; } ; }
2022-12-24 14:08:11 +01:00
# ["a" "b"] is equivalent to x.a.b
# 6 is a default value to return if the path does not exist in attrset
2016-02-28 22:49:42 +01:00
attrByPath [ " a " " b " ] 6 x
= > 3
attrByPath [ " z " " z " ] 6 x
= > 6
2021-06-08 16:03:12 +02:00
Type :
attrByPath : : [ String ] -> Any -> AttrSet -> Any
2022-12-24 14:08:11 +01:00
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
attrByPath =
# A list of strings representing the attribute path to return from `set`
attrPath :
# Default value if `attrPath` does not resolve to an existing value
default :
# The nested attribute set to select values from
set :
2023-11-26 13:42:32 +01:00
let
lenAttrPath = length attrPath ;
attrByPath' = n : s : (
if n == lenAttrPath then s
else (
let
attr = elemAt attrPath n ;
in
if s ? ${ attr } then attrByPath' ( n + 1 ) s . ${ attr }
else default
)
) ;
2009-02-09 17:51:03 +01:00
in
2023-11-26 13:42:32 +01:00
attrByPath' 0 set ;
2009-02-09 17:51:03 +01:00
2015-12-04 16:17:45 +01:00
/* R e t u r n i f a n a t t r i b u t e f r o m n e s t e d a t t r i b u t e s e t e x i s t s .
2016-02-28 22:49:42 +01:00
2023-12-13 18:38:41 +01:00
Nix has a [ has attribute operator ` ? ` ] ( https://nixos.org/manual/nix/stable/language/operators #has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example:
` ` ` nix
( x ? a . b ) == hasAttryByPath [ " a " " b " ] x
# and
( x ? ${ f p } . " e x a m p l e . c o m " ) == hasAttryByPath [ ( f p ) " e x a m p l e . c o m " ] x
` ` `
2023-12-08 23:15:53 +01:00
* * Laws * * :
1 . ` ` ` nix
hasAttrByPath [ ] x == true
` ` `
2016-02-28 22:49:42 +01:00
Example :
x = { a = { b = 3 ; } ; }
hasAttrByPath [ " a " " b " ] x
= > true
hasAttrByPath [ " z " " z " ] x
= > false
2023-12-08 23:15:53 +01:00
hasAttrByPath [ ] ( throw " n o n e e d " )
= > true
2016-02-28 22:49:42 +01:00
2021-06-08 16:03:12 +02:00
Type :
hasAttrByPath : : [ String ] -> AttrSet -> Bool
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
hasAttrByPath =
# A list of strings representing the attribute path to check from `set`
attrPath :
# The nested attribute set to check
e :
2023-11-26 13:41:01 +01:00
let
lenAttrPath = length attrPath ;
2023-11-26 13:42:32 +01:00
hasAttrByPath' = n : s : (
n == lenAttrPath || (
let
attr = elemAt attrPath n ;
in
if s ? ${ attr } then hasAttrByPath' ( n + 1 ) s . ${ attr }
else false
)
2023-11-26 13:41:01 +01:00
) ;
2015-12-04 16:17:45 +01:00
in
2023-11-26 13:41:01 +01:00
hasAttrByPath' 0 e ;
2012-10-23 15:35:48 +02:00
2023-12-08 22:59:40 +01:00
/*
Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets .
Can be used after [ ` mapAttrsRecursiveCond ` ] ( #function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition,
although this will evaluate the predicate function on sibling attributes as well .
Note that the empty attribute path is valid for all values , so this function only throws an exception if any of its inputs does .
* * Laws * * :
1 . ` ` ` nix
attrsets . longestValidPathPrefix [ ] x == [ ]
` ` `
2 . ` ` ` nix
hasAttrByPath ( attrsets . longestValidPathPrefix p x ) x == true
` ` `
Example :
x = { a = { b = 3 ; } ; }
attrsets . longestValidPathPrefix [ " a " " b " " c " ] x
= > [ " a " " b " ]
attrsets . longestValidPathPrefix [ " a " ] x
= > [ " a " ]
attrsets . longestValidPathPrefix [ " z " " z " ] x
= > [ ]
attrsets . longestValidPathPrefix [ " z " " z " ] ( throw " n o n e e d " )
= > [ ]
Type :
attrsets . longestValidPathPrefix : : [ String ] -> Value -> [ String ]
* /
longestValidPathPrefix =
# A list of strings representing the longest possible path that may be returned.
attrPath :
# The nested attribute set to check.
v :
let
lenAttrPath = length attrPath ;
getPrefixForSetAtIndex =
# The nested attribute set to check, if it is an attribute set, which
# is not a given.
remainingSet :
# The index of the attribute we're about to check, as well as
# the length of the prefix we've already checked.
remainingPathIndex :
if remainingPathIndex == lenAttrPath then
# All previously checked attributes exist, and no attr names left,
# so we return the whole path.
attrPath
else
let
attr = elemAt attrPath remainingPathIndex ;
in
if remainingSet ? ${ attr } then
getPrefixForSetAtIndex
remainingSet . ${ attr } # advance from the set to the attribute value
( remainingPathIndex + 1 ) # advance the path
else
# The attribute doesn't exist, so we return the prefix up to the
# previously checked length.
take remainingPathIndex attrPath ;
in
getPrefixForSetAtIndex v 0 ;
2021-06-08 16:03:12 +02:00
/* C r e a t e a n e w a t t r i b u t e s e t w i t h ` v a l u e ` s e t a t t h e n e s t e d a t t r i b u t e l o c a t i o n s p e c i f i e d i n ` a t t r P a t h ` .
2016-02-28 22:49:42 +01:00
Example :
setAttrByPath [ " a " " b " ] 3
= > { a = { b = 3 ; } ; }
2021-06-08 16:03:12 +02:00
Type :
setAttrByPath : : [ String ] -> Any -> AttrSet
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
setAttrByPath =
# A list of strings representing the attribute path to set
attrPath :
# The value to set at the location described by `attrPath`
value :
2021-08-20 18:24:39 +02:00
let
len = length attrPath ;
atDepth = n :
if n == len
then value
else { ${ elemAt attrPath n } = atDepth ( n + 1 ) ; } ;
in atDepth 0 ;
2009-03-07 00:21:14 +01:00
2023-01-01 22:51:54 +01:00
/* L i k e ` a t t r B y P a t h ` , b u t w i t h o u t a d e f a u l t v a l u e . I f i t d o e s n ' t f i n d t h e
2021-06-08 16:03:12 +02:00
path it will throw an error .
2016-02-28 22:49:42 +01:00
2023-12-13 18:38:41 +01:00
Nix has an [ attribute selection operator ] ( https://nixos.org/manual/nix/stable/language/operators #attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
` ` ` nix
x . a . b == getAttrByPath [ " a " " b " ] x
# and
x . ${ f p } . " e x a m p l e . c o m " == getAttrByPath [ ( f p ) " e x a m p l e . c o m " ] x
` ` `
2016-02-28 22:49:42 +01:00
Example :
x = { a = { b = 3 ; } ; }
getAttrFromPath [ " a " " b " ] x
= > 3
getAttrFromPath [ " z " " z " ] x
= > error : cannot find attribute ` z . z'
2021-06-08 16:03:12 +02:00
Type :
2022-12-24 14:08:11 +01:00
getAttrFromPath : : [ String ] -> AttrSet -> Any
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
getAttrFromPath =
# A list of strings representing the attribute path to get from `set`
attrPath :
# The nested attribute set to find the value in.
set :
2024-03-03 04:28:49 +01:00
attrByPath attrPath ( abort ( " c a n n o t f i n d a t t r i b u t e ` " + concatStringsSep " . " attrPath + " ' " ) ) set ;
2012-10-23 15:35:48 +02:00
2022-11-16 18:20:38 +01:00
/* M a p e a c h a t t r i b u t e i n t h e g i v e n s e t a n d m e r g e t h e m i n t o a n e w a t t r i b u t e s e t .
Type :
2022-12-24 14:08:11 +01:00
concatMapAttrs : : ( String -> a -> AttrSet ) -> AttrSet -> AttrSet
2022-11-16 18:20:38 +01:00
Example :
concatMapAttrs
( name : value : {
$ { name } = value ;
$ { name + value } = value ;
} )
{ x = " a " ; y = " b " ; }
= > { x = " a " ; xa = " a " ; y = " b " ; yb = " b " ; }
* /
2022-11-23 13:40:42 +01:00
concatMapAttrs = f : v :
foldl' mergeAttrs { }
( attrValues
( mapAttrs f v )
) ;
2022-11-16 18:20:38 +01:00
2009-03-07 00:21:14 +01:00
2021-11-22 20:34:01 +01:00
/* U p d a t e o r s e t s p e c i f i c p a t h s o f a n a t t r i b u t e s e t .
Takes a list of updates to apply and an attribute set to apply them to ,
and returns the attribute set with the updates applied . Updates are
2021-06-08 16:03:12 +02:00
represented as ` { path = . . . ; update = . . . ; } ` values , where ` path ` is a
2021-11-22 20:34:01 +01:00
list of strings representing the attribute path that should be updated ,
and ` update ` is a function that takes the old value at that attribute path
as an argument and returns the new
value it should be .
Properties :
2021-06-08 16:03:12 +02:00
2021-11-22 20:34:01 +01:00
- Updates to deeper attribute paths are applied before updates to more
shallow attribute paths
2021-06-08 16:03:12 +02:00
2021-11-22 20:34:01 +01:00
- Multiple updates to the same attribute path are applied in the order
they appear in the update list
2021-06-08 16:03:12 +02:00
2021-11-22 20:34:01 +01:00
- If any but the last ` path ` element leads into a value that is not an
attribute set , an error is thrown
2021-06-08 16:03:12 +02:00
2021-11-22 20:34:01 +01:00
- If there is an update for an attribute path that doesn't exist ,
accessing the argument in the update function causes an error , but
intermediate attribute sets are implicitly created as needed
Example :
updateManyAttrsByPath [
{
path = [ " a " " b " ] ;
update = old : { d = old . c ; } ;
}
{
path = [ " a " " b " " c " ] ;
update = old : old + 1 ;
}
{
path = [ " x " " y " ] ;
update = old : " x y " ;
}
] { a . b . c = 0 ; }
= > { a = { b = { d = 1 ; } ; } ; x = { y = " x y " ; } ; }
2021-06-08 16:03:12 +02:00
2023-01-30 22:22:02 +01:00
Type : updateManyAttrsByPath : : [ { path : : [ String ] ; update : : ( Any -> Any ) ; } ] -> AttrSet -> AttrSet
2021-11-22 20:34:01 +01:00
* /
updateManyAttrsByPath = let
# When recursing into attributes, instead of updating the `path` of each
# update using `tail`, which needs to allocate an entirely new list,
# we just pass a prefix length to use and make sure to only look at the
# path without the prefix length, so that we can reuse the original list
# entries.
go = prefixLength : hasValue : value : updates :
let
# Splits updates into ones on this level (split.right)
# And ones on levels further down (split.wrong)
split = partition ( el : length el . path == prefixLength ) updates ;
# Groups updates on further down levels into the attributes they modify
nested = groupBy ( el : elemAt el . path prefixLength ) split . wrong ;
# Applies only nested modification to the input value
withNestedMods =
# Return the value directly if we don't have any nested modifications
if split . wrong == [ ] then
if hasValue then value
else
# Throw an error if there is no value. This `head` call here is
# safe, but only in this branch since `go` could only be called
# with `hasValue == false` for nested updates, in which case
# it's also always called with at least one update
let updatePath = ( head split . right ) . path ; in
throw
( " u p d a t e M a n y A t t r s B y P a t h : P a t h ' ${ showAttrPath updatePath } ' d o e s "
+ " n o t e x i s t i n t h e g i v e n v a l u e , b u t t h e f i r s t u p d a t e t o t h i s "
+ " p a t h t r i e s t o a c c e s s t h e e x i s t i n g v a l u e . " )
else
# If there are nested modifications, try to apply them to the value
if ! hasValue then
# But if we don't have a value, just use an empty attribute set
# as the value, but simplify the code a bit
mapAttrs ( name : go ( prefixLength + 1 ) false null ) nested
else if isAttrs value then
# If we do have a value and it's an attribute set, override it
# with the nested modifications
value //
mapAttrs ( name : go ( prefixLength + 1 ) ( value ? ${ name } ) value . ${ name } ) nested
else
# However if it's not an attribute set, we can't apply the nested
# modifications, throw an error
let updatePath = ( head split . wrong ) . path ; in
throw
( " u p d a t e M a n y A t t r s B y P a t h : P a t h ' ${ showAttrPath updatePath } ' n e e d s t o "
+ " b e u p d a t e d , b u t p a t h ' ${ showAttrPath ( take prefixLength updatePath ) } ' "
+ " o f t h e g i v e n v a l u e i s n o t a n a t t r i b u t e s e t , s o w e c a n ' t "
+ " u p d a t e a n a t t r i b u t e i n s i d e o f i t . " ) ;
# We get the final result by applying all the updates on this level
# after having applied all the nested updates
# We use foldl instead of foldl' so that in case of multiple updates,
# intermediate values aren't evaluated if not needed
in foldl ( acc : el : el . update acc ) withNestedMods split . right ;
in updates : value : go 0 true value updates ;
2009-03-10 16:18:38 +01:00
/* R e t u r n t h e s p e c i f i e d a t t r i b u t e s f r o m a s e t .
2009-03-07 00:21:14 +01:00
2009-03-10 16:18:38 +01:00
Example :
attrVals [ " a " " b " " c " ] as
= > [ as . a as . b as . c ]
2021-06-08 16:03:12 +02:00
Type :
attrVals : : [ String ] -> AttrSet -> [ Any ]
2009-03-10 16:18:38 +01:00
* /
2021-06-08 16:03:12 +02:00
attrVals =
# The list of attributes to fetch from `set`. Each attribute name must exist on the attrbitue set
nameList :
# The set to get attribute values from
set : map ( x : set . ${ x } ) nameList ;
2009-02-09 17:51:03 +01:00
2009-03-10 16:18:38 +01:00
/* R e t u r n t h e v a l u e s o f a l l a t t r i b u t e s i n t h e g i v e n s e t , s o r t e d b y
attribute name .
Example :
attrValues { c = 3 ; a = 1 ; b = 2 ; }
= > [ 1 2 3 ]
2022-12-24 14:08:11 +01:00
2021-06-08 16:03:12 +02:00
Type :
attrValues : : AttrSet -> [ Any ]
2009-03-10 16:18:38 +01:00
* /
2024-02-09 05:33:27 +01:00
attrValues = builtins . attrValues ;
2009-03-10 16:18:38 +01:00
2018-12-11 22:18:22 +01:00
/* G i v e n a s e t o f a t t r i b u t e n a m e s , r e t u r n t h e s e t o f t h e c o r r e s p o n d i n g
attributes from the given set .
Example :
getAttrs [ " a " " b " ] { a = 1 ; b = 2 ; c = 3 ; }
= > { a = 1 ; b = 2 ; }
2021-06-08 16:03:12 +02:00
Type :
getAttrs : : [ String ] -> AttrSet -> AttrSet
2018-12-11 22:18:22 +01:00
* /
2021-06-08 16:03:12 +02:00
getAttrs =
# A list of attribute names to get out of `set`
names :
# The set to get the named attributes from
attrs : genAttrs names ( name : attrs . ${ name } ) ;
2018-12-11 22:18:22 +01:00
2023-01-01 22:51:54 +01:00
/* C o l l e c t e a c h a t t r i b u t e n a m e d ` a t t r ` f r o m a l i s t o f a t t r i b u t e
2009-03-10 16:18:38 +01:00
sets . Sets that don't contain the named attribute are ignored .
Example :
catAttrs " a " [ { a = 1 ; } { b = 0 ; } { a = 2 ; } ]
= > [ 1 2 ]
2021-06-08 16:03:12 +02:00
Type :
catAttrs : : String -> [ AttrSet ] -> [ Any ]
2009-03-10 16:18:38 +01:00
* /
2024-02-09 05:33:27 +01:00
catAttrs = builtins . catAttrs ;
2009-03-10 16:18:38 +01:00
2012-04-05 17:37:52 +02:00
/* F i l t e r a n a t t r i b u t e s e t b y r e m o v i n g a l l a t t r i b u t e s f o r w h i c h t h e
given predicate return false .
Example :
filterAttrs ( n : v : n == " f o o " ) { foo = 1 ; bar = 2 ; }
= > { foo = 1 ; }
2021-06-08 16:03:12 +02:00
Type :
filterAttrs : : ( String -> Any -> Bool ) -> AttrSet -> AttrSet
2012-04-05 17:37:52 +02:00
* /
2021-06-08 16:03:12 +02:00
filterAttrs =
# Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute.
pred :
# The attribute set to filter
set :
2015-07-23 16:12:25 +02:00
listToAttrs ( concatMap ( name : let v = set . ${ name } ; in if pred name v then [ ( nameValuePair name v ) ] else [ ] ) ( attrNames set ) ) ;
2012-04-05 17:37:52 +02:00
2012-10-23 15:35:48 +02:00
2017-04-19 21:41:28 +02:00
/* F i l t e r a n a t t r i b u t e s e t r e c u r s i v e l y b y r e m o v i n g a l l a t t r i b u t e s f o r
2015-09-19 00:16:06 +02:00
which the given predicate return false .
Example :
filterAttrsRecursive ( n : v : v != null ) { foo = { bar = null ; } ; }
= > { foo = { } ; }
2021-06-08 16:03:12 +02:00
Type :
filterAttrsRecursive : : ( String -> Any -> Bool ) -> AttrSet -> AttrSet
2015-09-19 00:16:06 +02:00
* /
2021-06-08 16:03:12 +02:00
filterAttrsRecursive =
# Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute.
pred :
# The attribute set to filter
set :
2015-09-19 00:16:06 +02:00
listToAttrs (
concatMap ( name :
let v = set . ${ name } ; in
if pred name v then [
( nameValuePair name (
if isAttrs v then filterAttrsRecursive pred v
else v
) )
] else [ ]
) ( attrNames set )
) ;
2023-02-28 11:04:19 +01:00
/*
2023-09-22 12:24:27 +02:00
Like [ ` lib . lists . foldl' ` ] ( #function-library-lib.lists.foldl-prime) but for attribute sets.
2023-02-28 11:04:19 +01:00
Iterates over every name-value pair in the given attribute set .
The result of the callback function is often called ` acc ` for accumulator . It is passed between callbacks from left to right and the final ` acc ` is the return value of ` foldlAttrs ` .
Attention :
There is a completely different function
` lib . foldAttrs `
which has nothing to do with this function , despite the similar name .
Example :
foldlAttrs
( acc : name : value : {
sum = acc . sum + value ;
names = acc . names ++ [ name ] ;
} )
{ sum = 0 ; names = [ ] ; }
{
foo = 1 ;
bar = 10 ;
}
->
{
sum = 11 ;
names = [ " b a r " " f o o " ] ;
}
foldlAttrs
( throw " f u n c t i o n n o t n e e d e d " )
123
{ } ;
->
123
foldlAttrs
2023-09-22 12:24:27 +02:00
( acc : _ : _ : acc )
3
{ z = throw " v a l u e n o t n e e d e d " ; a = throw " v a l u e n o t n e e d e d " ; } ;
2023-02-28 11:04:19 +01:00
->
3
The accumulator doesn't have to be an attrset .
It can be as simple as a number or string .
foldlAttrs
( acc : _ : v : acc * 10 + v )
1
{ z = 1 ; a = 2 ; } ;
->
121
Type :
foldlAttrs : : ( a -> String -> b -> a ) -> a -> { . . . : : b } -> a
* /
foldlAttrs = f : init : set :
2023-09-22 12:24:27 +02:00
foldl'
2023-02-28 11:04:19 +01:00
( acc : name : f acc name set . ${ name } )
init
( attrNames set ) ;
2016-02-28 22:49:42 +01:00
/* A p p l y f o l d f u n c t i o n s t o v a l u e s g r o u p e d b y k e y .
Example :
2022-05-10 06:52:20 +02:00
foldAttrs ( item : acc : [ item ] ++ acc ) [ ] [ { a = 2 ; } { a = 3 ; } ]
2016-02-28 22:49:42 +01:00
= > { a = [ 2 3 ] ; }
2021-06-08 16:03:12 +02:00
Type :
foldAttrs : : ( Any -> Any -> Any ) -> Any -> [ AttrSets ] -> Any
2022-12-24 14:08:11 +01:00
2012-08-28 14:40:24 +02:00
* /
2021-06-08 16:03:12 +02:00
foldAttrs =
# A function, given a value and a collector combines the two.
op :
# The starting value.
nul :
# A list of attribute sets to fold together by key.
list_of_attrs :
2021-01-25 07:57:48 +01:00
foldr ( n : a :
foldr ( name : o :
2018-08-14 01:01:08 +02:00
o // { ${ name } = op n . ${ name } ( a . ${ name } or nul ) ; }
2012-08-28 14:40:24 +02:00
) a ( attrNames n )
2021-06-08 16:03:12 +02:00
) { } list_of_attrs ;
2012-04-05 17:37:52 +02:00
2012-10-23 15:35:48 +02:00
2023-01-01 22:51:54 +01:00
/* R e c u r s i v e l y c o l l e c t s e t s t h a t v e r i f y a g i v e n p r e d i c a t e n a m e d ` p r e d `
from the set ` attrs ` . The recursion is stopped when the predicate is
2009-06-11 18:03:33 +02:00
verified .
Example :
2013-11-12 13:48:19 +01:00
collect isList { a = { b = [ " b " ] ; } ; c = [ 1 ] ; }
2009-09-29 17:34:19 +02:00
= > [ [ " b " ] [ 1 ] ]
2009-06-11 18:03:33 +02:00
collect ( x : x ? outPath )
{ a = { outPath = " a / " ; } ; b = { outPath = " b / " ; } ; }
= > [ { outPath = " a / " ; } { outPath = " b / " ; } ]
2021-06-08 16:03:12 +02:00
Type :
collect : : ( AttrSet -> Bool ) -> AttrSet -> [ x ]
2009-06-11 18:03:33 +02:00
* /
2021-06-08 16:03:12 +02:00
collect =
# Given an attribute's value, determine if recursion should stop.
pred :
# The attribute set to recursively collect.
attrs :
2009-06-11 18:03:33 +02:00
if pred attrs then
[ attrs ]
2013-11-12 13:50:45 +01:00
else if isAttrs attrs then
2009-06-11 18:03:33 +02:00
concatMap ( collect pred ) ( attrValues attrs )
else
[ ] ;
2021-01-25 16:59:46 +01:00
/* R e t u r n t h e c a r t e s i a n p r o d u c t o f a t t r i b u t e s e t v a l u e c o m b i n a t i o n s .
Example :
cartesianProductOfSets { a = [ 1 2 ] ; b = [ 10 20 ] ; }
= > [
{ a = 1 ; b = 10 ; }
{ a = 1 ; b = 20 ; }
{ a = 2 ; b = 10 ; }
{ a = 2 ; b = 20 ; }
]
2021-06-08 16:03:12 +02:00
Type :
2022-12-24 14:08:11 +01:00
cartesianProductOfSets : : AttrSet -> [ AttrSet ]
2021-01-25 16:59:46 +01:00
* /
2021-06-08 16:03:12 +02:00
cartesianProductOfSets =
# Attribute set with attributes that are lists of values
attrsOfLists :
2021-08-20 18:24:39 +02:00
foldl' ( listOfAttrs : attrName :
2021-01-25 16:59:46 +01:00
concatMap ( attrs :
map ( listValue : attrs // { ${ attrName } = listValue ; } ) attrsOfLists . ${ attrName }
) listOfAttrs
) [ { } ] ( attrNames attrsOfLists ) ;
2009-06-11 18:03:33 +02:00
2021-06-08 16:03:12 +02:00
/* U t i l i t y f u n c t i o n t h a t c r e a t e s a ` { n a m e , v a l u e } ` p a i r a s e x p e c t e d b y ` b u i l t i n s . l i s t T o A t t r s ` .
2016-02-28 22:49:42 +01:00
Example :
nameValuePair " s o m e " 6
= > { name = " s o m e " ; value = 6 ; }
2021-06-08 16:03:12 +02:00
Type :
2023-01-30 22:22:02 +01:00
nameValuePair : : String -> Any -> { name : : String ; value : : Any ; }
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
nameValuePair =
# Attribute name
name :
# Attribute value
value :
{ inherit name value ; } ;
2009-03-10 16:18:38 +01:00
2012-10-23 15:35:48 +02:00
2021-06-08 16:03:12 +02:00
/* A p p l y a f u n c t i o n t o e a c h e l e m e n t i n a n a t t r i b u t e s e t , c r e a t i n g a n e w a t t r i b u t e s e t .
2009-03-10 16:18:38 +01:00
Example :
mapAttrs ( name : value : name + " - " + value )
2012-05-25 19:01:58 +02:00
{ x = " f o o " ; y = " b a r " ; }
= > { x = " x - f o o " ; y = " y - b a r " ; }
2021-06-08 16:03:12 +02:00
Type :
mapAttrs : : ( String -> Any -> Any ) -> AttrSet -> AttrSet
2009-03-10 16:18:38 +01:00
* /
2024-02-09 05:33:27 +01:00
mapAttrs = builtins . mapAttrs ;
2012-05-25 19:01:58 +02:00
2023-01-01 22:51:54 +01:00
/* L i k e ` m a p A t t r s ` , b u t a l l o w s t h e n a m e o f e a c h a t t r i b u t e t o b e
2012-05-25 19:01:58 +02:00
changed in addition to the value . The applied function should
2023-01-01 22:51:54 +01:00
return both the new name and value as a ` nameValuePair ` .
2012-10-23 15:35:48 +02:00
2012-05-25 19:01:58 +02:00
Example :
mapAttrs' ( name : value : nameValuePair ( " f o o _ " + name ) ( " b a r - " + value ) )
{ x = " a " ; y = " b " ; }
= > { foo_x = " b a r - a " ; foo_y = " b a r - b " ; }
2021-06-08 16:03:12 +02:00
Type :
2023-01-30 22:22:02 +01:00
mapAttrs' : : ( String -> Any -> { name : : String ; value : : Any ; } ) -> AttrSet -> AttrSet
2012-05-25 19:01:58 +02:00
* /
2021-06-08 16:03:12 +02:00
mapAttrs' =
# A function, given an attribute's name and value, returns a new `nameValuePair`.
f :
# Attribute set to map over.
set :
2014-10-05 00:03:52 +02:00
listToAttrs ( map ( attr : f attr set . ${ attr } ) ( attrNames set ) ) ;
2012-10-23 15:35:48 +02:00
2009-03-10 16:18:38 +01:00
2012-06-14 21:07:01 +02:00
/* C a l l a f u n c t i o n f o r e a c h a t t r i b u t e i n t h e g i v e n s e t a n d r e t u r n
the result in a list .
2012-10-23 15:35:48 +02:00
2012-06-14 21:07:01 +02:00
Example :
mapAttrsToList ( name : value : name + value )
{ x = " a " ; y = " b " ; }
= > [ " x a " " y b " ]
2021-06-08 16:03:12 +02:00
Type :
mapAttrsToList : : ( String -> a -> b ) -> AttrSet -> [ b ]
2012-06-14 21:07:01 +02:00
* /
2021-06-08 16:03:12 +02:00
mapAttrsToList =
# A function, given an attribute's name and value, returns a new value.
f :
# Attribute set to map over.
attrs :
2014-10-05 00:03:52 +02:00
map ( name : f name attrs . ${ name } ) ( attrNames attrs ) ;
2012-10-23 15:35:48 +02:00
2023-09-10 17:56:29 +02:00
/*
Deconstruct an attrset to a list of name-value pairs as expected by [ ` builtins . listToAttrs ` ] ( https://nixos.org/manual/nix/stable/language/builtins.html #builtins-listToAttrs).
Each element of the resulting list is an attribute set with these attributes :
- ` name ` ( string ) : The name of the attribute
- ` value ` ( any ) : The value of the attribute
The following is always true :
` ` ` nix
builtins . listToAttrs ( attrsToList attrs ) == attrs
` ` `
: : : { . warning }
The opposite is not always true . In general expect that
` ` ` nix
attrsToList ( builtins . listToAttrs list ) != list
` ` `
This is because the ` listToAttrs ` removes duplicate names and doesn't preserve the order of the list .
: : :
Example :
attrsToList { foo = 1 ; bar = " a s d f " ; }
= > [ { name = " b a r " ; value = " a s d f " ; } { name = " f o o " ; value = 1 ; } ]
Type :
attrsToList : : AttrSet -> [ { name : : String ; value : : Any ; } ]
* /
attrsToList = mapAttrsToList nameValuePair ;
2012-06-14 21:07:01 +02:00
2024-03-06 10:21:23 +01:00
/* *
Like ` mapAttrs ` , except that it recursively applies itself to the * leaf * attributes of a potentially-nested attribute set :
the second argument of the function will never be an attrset .
Also , the first argument of the mapping function is a * list * of the attribute names that form the path to the leaf attribute .
2023-01-25 03:52:54 +01:00
2024-03-06 10:21:23 +01:00
For a function that gives you control over what counts as a leaf , see ` mapAttrsRecursiveCond ` .
2009-03-30 15:19:57 +02:00
2024-03-06 10:21:23 +01:00
: : : { #map-attrs-recursive-example .example}
# Map over leaf attributes
2021-06-08 16:03:12 +02:00
2024-03-06 10:21:23 +01:00
` ` ` nix
mapAttrsRecursive ( path : value : concatStringsSep " - " ( path ++ [ value ] ) )
{ n = { a = " A " ; m = { b = " B " ; c = " C " ; } ; } ; d = " D " ; }
` ` `
evaluates to
` ` ` nix
{ n = { a = " n - a - A " ; m = { b = " n - m - b - B " ; c = " n - m - c - C " ; } ; } ; d = " d - D " ; }
` ` `
: : :
# Type
` ` `
mapAttrsRecursive : : ( [ String ] -> a -> b ) -> AttrSet -> AttrSet
` ` `
2009-03-10 16:18:38 +01:00
* /
2021-06-08 16:03:12 +02:00
mapAttrsRecursive =
2024-03-06 10:21:23 +01:00
# A function that, given an attribute path as a list of strings and the corresponding attribute value, returns a new value.
2021-06-08 16:03:12 +02:00
f :
2024-03-06 10:21:23 +01:00
# Attribute set to recursively map over.
2021-06-08 16:03:12 +02:00
set :
mapAttrsRecursiveCond ( as : true ) f set ;
2009-03-30 15:19:57 +02:00
2012-10-23 15:35:48 +02:00
2024-03-06 10:21:23 +01:00
/* *
Like ` mapAttrsRecursive ` , but it takes an additional predicate that tells it whether to recurse into an attribute set .
If the predicate returns false , ` mapAttrsRecursiveCond ` does not recurse , but instead applies the mapping function .
If the predicate returns true , it does recurse , and does not apply the mapping function .
2009-03-30 15:19:57 +02:00
2024-03-06 10:21:23 +01:00
: : : { #map-attrs-recursive-cond-example .example}
# Map over an leaf attributes defined by a condition
2021-06-08 16:03:12 +02:00
2024-03-06 10:21:23 +01:00
Map derivations to their ` name ` attribute .
Derivatons are identified as attribute sets that contain ` { type = " d e r i v a t i o n " ; } ` .
` ` ` nix
mapAttrsRecursiveCond
( as : ! ( as ? " t y p e " && as . type == " d e r i v a t i o n " ) )
( x : x . name )
attrs
` ` `
: : :
# Type
` ` `
mapAttrsRecursiveCond : : ( AttrSet -> Bool ) -> ( [ String ] -> a -> b ) -> AttrSet -> AttrSet
` ` `
2013-03-06 16:33:01 +01:00
* /
2021-06-08 16:03:12 +02:00
mapAttrsRecursiveCond =
2024-03-06 10:21:23 +01:00
# A function that, given the attribute set the recursion is currently at, determines if to recurse deeper into that attribute set.
2021-06-08 16:03:12 +02:00
cond :
2024-03-06 10:21:23 +01:00
# A function that, given an attribute path as a list of strings and the corresponding attribute value, returns a new value.
# The attribute value is either an attribute set for which `cond` returns false, or something other than an attribute set.
2021-06-08 16:03:12 +02:00
f :
# Attribute set to recursively map over.
set :
2009-03-10 16:18:38 +01:00
let
2021-12-27 23:53:15 +01:00
recurse = path :
2024-03-03 04:15:31 +01:00
mapAttrs
( name : value :
2009-03-30 15:19:57 +02:00
if isAttrs value && cond value
2024-03-03 04:15:31 +01:00
then recurse ( path ++ [ name ] ) value
else f ( path ++ [ name ] ) value ) ;
in
recurse [ ] set ;
2009-04-25 16:08:29 +02:00
2013-03-06 16:33:01 +01:00
/* G e n e r a t e a n a t t r i b u t e s e t b y m a p p i n g a f u n c t i o n o v e r a l i s t o f
attribute names .
Example :
genAttrs [ " f o o " " b a r " ] ( name : " x _ " + name )
= > { foo = " x _ f o o " ; bar = " x _ b a r " ; }
2021-06-08 16:03:12 +02:00
Type :
genAttrs : : [ String ] -> ( String -> Any ) -> AttrSet
2013-03-06 16:33:01 +01:00
* /
2021-06-08 16:03:12 +02:00
genAttrs =
# Names of values in the resulting attribute set.
names :
# A function, given the name of the attribute, returns the attribute's value.
f :
2013-03-06 16:33:01 +01:00
listToAttrs ( map ( n : nameValuePair n ( f n ) ) names ) ;
2016-02-28 22:49:42 +01:00
/* C h e c k w h e t h e r t h e a r g u m e n t i s a d e r i v a t i o n . A n y s e t w i t h
2021-06-08 16:03:12 +02:00
` { type = " d e r i v a t i o n " ; } ` counts as a derivation .
2009-04-25 16:08:29 +02:00
2016-02-28 22:49:42 +01:00
Example :
nixpkgs = import <nixpkgs> { }
isDerivation nixpkgs . ruby
= > true
isDerivation " f o o b a r "
= > false
2021-06-08 16:03:12 +02:00
Type :
isDerivation : : Any -> Bool
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
isDerivation =
# Value to check.
value : value . type or null == " d e r i v a t i o n " ;
2009-09-10 12:52:51 +02:00
2021-06-08 16:03:12 +02:00
/* C o n v e r t s a s t o r e p a t h t o a f a k e d e r i v a t i o n .
Type :
toDerivation : : Path -> Derivation
* /
toDerivation =
# A store path to convert to a derivation.
path :
let
path' = builtins . storePath path ;
res =
{ type = " d e r i v a t i o n " ;
name = sanitizeDerivationName ( builtins . substring 33 ( -1 ) ( baseNameOf path' ) ) ;
outPath = path' ;
outputs = [ " o u t " ] ;
out = res ;
outputName = " o u t " ;
} ;
2016-09-19 11:51:11 +02:00
in res ;
2015-08-06 19:55:42 +02:00
2021-06-08 16:03:12 +02:00
/* I f ` c o n d ` i s t r u e , r e t u r n t h e a t t r i b u t e s e t ` a s ` ,
2016-02-28 22:49:42 +01:00
otherwise an empty attribute set .
Example :
optionalAttrs ( true ) { my = " s e t " ; }
= > { my = " s e t " ; }
optionalAttrs ( false ) { my = " s e t " ; }
= > { }
2021-06-08 16:03:12 +02:00
Type :
2022-12-24 14:08:11 +01:00
optionalAttrs : : Bool -> AttrSet -> AttrSet
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
optionalAttrs =
# Condition under which the `as` attribute set is returned.
cond :
# The attribute set to return if `cond` is `true`.
as :
if cond then as else { } ;
2009-09-29 16:57:00 +02:00
2021-06-08 16:03:12 +02:00
/* M e r g e s e t s o f a t t r i b u t e s a n d u s e t h e f u n c t i o n ` f ` t o m e r g e a t t r i b u t e s
2016-02-28 22:49:42 +01:00
values .
Example :
zipAttrsWithNames [ " a " ] ( name : vs : vs ) [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
= > { a = [ " x " " y " ] ; }
2021-06-08 16:03:12 +02:00
Type :
zipAttrsWithNames : : [ String ] -> ( String -> [ Any ] -> Any ) -> [ AttrSet ] -> AttrSet
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
zipAttrsWithNames =
# List of attribute names to zip.
names :
# A function, accepts an attribute name, all the values, and returns a combined value.
f :
# List of values from the list of attribute sets.
sets :
2009-09-29 16:57:00 +02:00
listToAttrs ( map ( name : {
inherit name ;
value = f name ( catAttrs name sets ) ;
} ) names ) ;
2021-06-08 16:03:12 +02:00
/* M e r g e s e t s o f a t t r i b u t e s a n d u s e t h e f u n c t i o n f t o m e r g e a t t r i b u t e v a l u e s .
Like ` lib . attrsets . zipAttrsWithNames ` with all key names are passed for ` names ` .
Implementation note : Common names appear multiple times in the list of
2016-02-28 22:49:42 +01:00
names , hopefully this does not affect the system because the maximal
2021-06-08 16:03:12 +02:00
laziness avoid computing twice the same expression and ` listToAttrs ` does
2016-02-28 22:49:42 +01:00
not care about duplicated attribute names .
Example :
zipAttrsWith ( name : values : values ) [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
2023-01-30 22:22:02 +01:00
= > { a = [ " x " " y " ] ; b = [ " z " ] ; }
2021-06-08 16:03:12 +02:00
Type :
2022-12-24 14:08:11 +01:00
zipAttrsWith : : ( String -> [ Any ] -> Any ) -> [ AttrSet ] -> AttrSet
2016-02-28 22:49:42 +01:00
* /
2021-12-25 15:20:26 +01:00
zipAttrsWith =
builtins . zipAttrsWith or ( f : sets : zipAttrsWithNames ( concatMap attrNames sets ) f sets ) ;
2009-09-29 16:57:00 +02:00
2021-06-08 16:03:12 +02:00
/* M e r g e s e t s o f a t t r i b u t e s a n d c o m b i n e e a c h a t t r i b u t e v a l u e i n t o a l i s t .
2023-01-01 22:51:54 +01:00
Like ` lib . attrsets . zipAttrsWith ` with ` ( name : values : values ) ` as the function .
2021-06-08 16:03:12 +02:00
Example :
zipAttrs [ { a = " x " ; } { a = " y " ; b = " z " ; } ]
2023-01-30 22:22:02 +01:00
= > { a = [ " x " " y " ] ; b = [ " z " ] ; }
2021-06-08 16:03:12 +02:00
Type :
zipAttrs : : [ AttrSet ] -> AttrSet
2016-02-28 22:49:42 +01:00
* /
2024-03-03 04:09:40 +01:00
zipAttrs = zipAttrsWith ( name : values : values ) ;
2021-06-08 16:03:12 +02:00
2023-07-18 18:24:45 +02:00
/*
Merge a list of attribute sets together using the ` // ` operator .
In case of duplicate attributes , values from later list elements take precedence over earlier ones .
The result is the same as ` foldl mergeAttrs { } ` , but the performance is better for large inputs .
For n list elements , each with an attribute set containing m unique attributes , the complexity of this operation is O ( nm log n ) .
Type :
mergeAttrsList : : [ Attrs ] -> Attrs
Example :
mergeAttrsList [ { a = 0 ; b = 1 ; } { c = 2 ; d = 3 ; } ]
= > { a = 0 ; b = 1 ; c = 2 ; d = 3 ; }
mergeAttrsList [ { a = 0 ; } { a = 1 ; } ]
= > { a = 1 ; }
* /
mergeAttrsList = list :
let
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
# Type: Int -> Int -> Attrs
binaryMerge = start : end :
# assert start < end; # Invariant
if end - start >= 2 then
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
# The invariant is satisfied because each half will have at least 1 element
binaryMerge start ( start + ( end - start ) / 2 )
// binaryMerge ( start + ( end - start ) / 2 ) end
else
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
elemAt list start ;
in
if list == [ ] then
# Calling binaryMerge as below would not satisfy its invariant
{ }
else
binaryMerge 0 ( length list ) ;
2009-09-29 16:57:00 +02:00
/* D o e s t h e s a m e a s t h e u p d a t e o p e r a t o r ' / / ' e x c e p t t h a t a t t r i b u t e s a r e
2017-04-19 21:41:28 +02:00
merged until the given predicate is verified . The predicate should
2009-11-19 17:07:47 +01:00
accept 3 arguments which are the path to reach the attribute , a part of
2009-09-29 16:57:00 +02:00
the first attribute set and a part of the second attribute set . When
2021-06-08 16:03:12 +02:00
the predicate is satisfied , the value of the first attribute set is
2009-09-29 16:57:00 +02:00
replaced by the value of the second attribute set .
Example :
recursiveUpdateUntil ( path : l : r : path == [ " f o o " ] ) {
# first attribute set
foo . bar = 1 ;
foo . baz = 2 ;
bar = 3 ;
} {
#second attribute set
foo . bar = 1 ;
foo . quz = 2 ;
baz = 4 ;
}
2021-06-08 16:03:12 +02:00
= > {
2009-09-29 16:57:00 +02:00
foo . bar = 1 ; # 'foo.*' from the second set
2012-10-23 15:35:48 +02:00
foo . quz = 2 ; #
2009-09-29 16:57:00 +02:00
bar = 3 ; # 'bar' from the first set
baz = 4 ; # 'baz' from the second set
}
2021-06-08 16:03:12 +02:00
Type :
recursiveUpdateUntil : : ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet
* /
recursiveUpdateUntil =
# Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments.
pred :
# Left attribute set of the merge.
lhs :
# Right attribute set of the merge.
rhs :
2009-09-29 16:57:00 +02:00
let f = attrPath :
zipAttrsWith ( n : values :
2018-06-06 22:38:40 +02:00
let here = attrPath ++ [ n ] ; in
2021-12-27 23:16:14 +01:00
if length values == 1
|| pred here ( elemAt values 1 ) ( head values ) then
2009-09-29 16:57:00 +02:00
head values
else
2018-06-06 22:38:40 +02:00
f here values
2009-09-29 16:57:00 +02:00
) ;
in f [ ] [ rhs lhs ] ;
2021-06-08 16:03:12 +02:00
2016-12-04 01:39:03 +01:00
/* A r e c u r s i v e v a r i a n t o f t h e u p d a t e o p e r a t o r ‘ / / ’ . T h e r e c u r s i o n
2012-11-14 11:38:47 +01:00
stops when one of the attribute values is not an attribute set ,
in which case the right hand side value takes precedence over the
2009-09-29 16:57:00 +02:00
left hand side value .
Example :
recursiveUpdate {
boot . loader . grub . enable = true ;
boot . loader . grub . device = " / d e v / h d a " ;
} {
boot . loader . grub . device = " " ;
}
returns : {
boot . loader . grub . enable = true ;
boot . loader . grub . device = " " ;
}
2021-06-08 16:03:12 +02:00
Type :
2022-12-24 14:08:11 +01:00
recursiveUpdate : : AttrSet -> AttrSet -> AttrSet
2021-06-08 16:03:12 +02:00
* /
recursiveUpdate =
# Left attribute set of the merge.
lhs :
# Right attribute set of the merge.
rhs :
recursiveUpdateUntil ( path : lhs : rhs : ! ( isAttrs lhs && isAttrs rhs ) ) lhs rhs ;
2009-09-29 16:57:00 +02:00
lib.attrsets.matchAttrs: Avoid some list allocations when walking structure
Benchmarks (`nix-instantiate ./. -A python3`):
- Before:
``` json
{
"cpuTime": 0.29049500823020935,
"envs": {
"bytes": 4484216,
"elements": 221443,
"number": 169542
},
"gc": {
"heapSize": 402915328,
"totalBytes": 53086800
},
"list": {
"bytes": 749424,
"concats": 4242,
"elements": 93678
},
"nrAvoided": 253991,
"nrFunctionCalls": 149848,
"nrLookups": 49612,
"nrOpUpdateValuesCopied": 1587837,
"nrOpUpdates": 10104,
"nrPrimOpCalls": 130356,
"nrThunks": 358981,
"sets": {
"bytes": 30423600,
"elements": 1859999,
"number": 41476
},
"sizes": {
"Attr": 16,
"Bindings": 16,
"Env": 16,
"Value": 24
},
"symbols": {
"bytes": 236145,
"number": 24453
},
"values": {
"bytes": 10502520,
"number": 437605
}
}
```
- After:
``` json
{
"cpuTime": 0.2946169972419739,
"envs": {
"bytes": 3315224,
"elements": 172735,
"number": 120834
},
"gc": {
"heapSize": 402915328,
"totalBytes": 48718432
},
"list": {
"bytes": 347568,
"concats": 4242,
"elements": 43446
},
"nrAvoided": 173252,
"nrFunctionCalls": 101140,
"nrLookups": 73595,
"nrOpUpdateValuesCopied": 1587837,
"nrOpUpdates": 10104,
"nrPrimOpCalls": 83067,
"nrThunks": 304216,
"sets": {
"bytes": 29704096,
"elements": 1831673,
"number": 24833
},
"sizes": {
"Attr": 16,
"Bindings": 16,
"Env": 16,
"Value": 24
},
"symbols": {
"bytes": 236145,
"number": 24453
},
"values": {
"bytes": 8961552,
"number": 373398
}
}
```
2023-11-24 04:10:13 +01:00
/*
Recurse into every attribute set of the first argument and check that :
- Each attribute path also exists in the second argument .
- If the attribute's value is not a nested attribute set , it must have the same value in the right argument .
2016-02-28 22:49:42 +01:00
Example :
2017-04-01 21:34:38 +02:00
matchAttrs { cpu = { } ; } { cpu = { bits = 64 ; } ; }
2016-02-28 22:49:42 +01:00
= > true
2021-06-08 16:03:12 +02:00
Type :
matchAttrs : : AttrSet -> AttrSet -> Bool
* /
matchAttrs =
2022-12-18 00:59:29 +01:00
# Attribute set structure to match
2021-06-08 16:03:12 +02:00
pattern :
lib.attrsets.matchAttrs: Avoid some list allocations when walking structure
Benchmarks (`nix-instantiate ./. -A python3`):
- Before:
``` json
{
"cpuTime": 0.29049500823020935,
"envs": {
"bytes": 4484216,
"elements": 221443,
"number": 169542
},
"gc": {
"heapSize": 402915328,
"totalBytes": 53086800
},
"list": {
"bytes": 749424,
"concats": 4242,
"elements": 93678
},
"nrAvoided": 253991,
"nrFunctionCalls": 149848,
"nrLookups": 49612,
"nrOpUpdateValuesCopied": 1587837,
"nrOpUpdates": 10104,
"nrPrimOpCalls": 130356,
"nrThunks": 358981,
"sets": {
"bytes": 30423600,
"elements": 1859999,
"number": 41476
},
"sizes": {
"Attr": 16,
"Bindings": 16,
"Env": 16,
"Value": 24
},
"symbols": {
"bytes": 236145,
"number": 24453
},
"values": {
"bytes": 10502520,
"number": 437605
}
}
```
- After:
``` json
{
"cpuTime": 0.2946169972419739,
"envs": {
"bytes": 3315224,
"elements": 172735,
"number": 120834
},
"gc": {
"heapSize": 402915328,
"totalBytes": 48718432
},
"list": {
"bytes": 347568,
"concats": 4242,
"elements": 43446
},
"nrAvoided": 173252,
"nrFunctionCalls": 101140,
"nrLookups": 73595,
"nrOpUpdateValuesCopied": 1587837,
"nrOpUpdates": 10104,
"nrPrimOpCalls": 83067,
"nrThunks": 304216,
"sets": {
"bytes": 29704096,
"elements": 1831673,
"number": 24833
},
"sizes": {
"Attr": 16,
"Bindings": 16,
"Env": 16,
"Value": 24
},
"symbols": {
"bytes": 236145,
"number": 24453
},
"values": {
"bytes": 8961552,
"number": 373398
}
}
```
2023-11-24 04:10:13 +01:00
# Attribute set to check
2021-06-08 16:03:12 +02:00
attrs :
assert isAttrs pattern ;
lib.attrsets.matchAttrs: Avoid some list allocations when walking structure
Benchmarks (`nix-instantiate ./. -A python3`):
- Before:
``` json
{
"cpuTime": 0.29049500823020935,
"envs": {
"bytes": 4484216,
"elements": 221443,
"number": 169542
},
"gc": {
"heapSize": 402915328,
"totalBytes": 53086800
},
"list": {
"bytes": 749424,
"concats": 4242,
"elements": 93678
},
"nrAvoided": 253991,
"nrFunctionCalls": 149848,
"nrLookups": 49612,
"nrOpUpdateValuesCopied": 1587837,
"nrOpUpdates": 10104,
"nrPrimOpCalls": 130356,
"nrThunks": 358981,
"sets": {
"bytes": 30423600,
"elements": 1859999,
"number": 41476
},
"sizes": {
"Attr": 16,
"Bindings": 16,
"Env": 16,
"Value": 24
},
"symbols": {
"bytes": 236145,
"number": 24453
},
"values": {
"bytes": 10502520,
"number": 437605
}
}
```
- After:
``` json
{
"cpuTime": 0.2946169972419739,
"envs": {
"bytes": 3315224,
"elements": 172735,
"number": 120834
},
"gc": {
"heapSize": 402915328,
"totalBytes": 48718432
},
"list": {
"bytes": 347568,
"concats": 4242,
"elements": 43446
},
"nrAvoided": 173252,
"nrFunctionCalls": 101140,
"nrLookups": 73595,
"nrOpUpdateValuesCopied": 1587837,
"nrOpUpdates": 10104,
"nrPrimOpCalls": 83067,
"nrThunks": 304216,
"sets": {
"bytes": 29704096,
"elements": 1831673,
"number": 24833
},
"sizes": {
"Attr": 16,
"Bindings": 16,
"Env": 16,
"Value": 24
},
"symbols": {
"bytes": 236145,
"number": 24453
},
"values": {
"bytes": 8961552,
"number": 373398
}
}
```
2023-11-24 04:10:13 +01:00
all
( # Compare equality between `pattern` & `attrs`.
attr :
# Missing attr, not equal.
attrs ? ${ attr } && (
let
lhs = pattern . ${ attr } ;
rhs = attrs . ${ attr } ;
in
# If attrset check recursively
if isAttrs lhs then isAttrs rhs && matchAttrs lhs rhs
else lhs == rhs
)
)
( attrNames pattern ) ;
2021-06-08 16:03:12 +02:00
2016-02-28 22:49:42 +01:00
/* O v e r r i d e o n l y t h e a t t r i b u t e s t h a t a r e a l r e a d y p r e s e n t i n t h e o l d s e t
useful for deep-overriding .
Example :
2018-09-17 23:28:47 +02:00
overrideExisting { } { a = 1 ; }
= > { }
overrideExisting { b = 2 ; } { a = 1 ; }
= > { b = 2 ; }
overrideExisting { a = 3 ; b = 2 ; } { a = 1 ; }
= > { a = 1 ; b = 2 ; }
2021-06-08 16:03:12 +02:00
Type :
overrideExisting : : AttrSet -> AttrSet -> AttrSet
2016-02-28 22:49:42 +01:00
* /
2021-06-08 16:03:12 +02:00
overrideExisting =
# Original attribute set
old :
# Attribute set with attributes to override in `old`.
new :
2018-09-07 20:59:12 +02:00
mapAttrs ( name : value : new . ${ name } or value ) old ;
2010-07-08 17:31:59 +02:00
2021-06-08 16:03:12 +02:00
2021-12-02 18:12:51 +01:00
/* T u r n s a l i s t o f s t r i n g s i n t o a h u m a n - r e a d a b l e d e s c r i p t i o n o f t h o s e
strings represented as an attribute path . The result of this function is
not intended to be machine-readable .
2022-12-24 14:08:11 +01:00
Create a new attribute set with ` value ` set at the nested attribute location specified in ` attrPath ` .
2021-12-02 18:12:51 +01:00
Example :
showAttrPath [ " f o o " " 1 0 " " b a r " ]
= > " f o o . \" 1 0 \" . b a r "
showAttrPath [ ]
= > " < r o o t a t t r i b u t e p a t h > "
2021-06-08 16:03:12 +02:00
Type :
showAttrPath : : [ String ] -> String
2021-12-02 18:12:51 +01:00
* /
2021-06-08 16:03:12 +02:00
showAttrPath =
# Attribute path to render to a string
path :
2021-12-02 18:12:51 +01:00
if path == [ ] then " < r o o t a t t r i b u t e p a t h > "
else concatMapStringsSep " . " escapeNixIdentifier path ;
2021-06-08 16:03:12 +02:00
2016-04-14 17:13:50 +02:00
/* G e t a p a c k a g e o u t p u t .
If no output is found , fallback to ` . out ` and then to the default .
Example :
getOutput " d e v " pkgs . openssl
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - d e v "
2021-06-08 16:03:12 +02:00
Type :
getOutput : : String -> Derivation -> String
2016-04-14 17:13:50 +02:00
* /
getOutput = output : pkg :
2021-10-15 16:30:58 +02:00
if ! pkg ? outputSpecified || ! pkg . outputSpecified
2016-04-14 17:13:50 +02:00
then pkg . ${ output } or pkg . out or pkg
else pkg ;
2021-06-08 16:03:12 +02:00
/* G e t a p a c k a g e ' s ` b i n ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-24 14:08:11 +01:00
getBin pkgs . openssl
2021-06-08 16:03:12 +02:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r "
Type :
2022-12-24 14:08:11 +01:00
getBin : : Derivation -> String
2021-06-08 16:03:12 +02:00
* /
2016-04-14 18:00:39 +02:00
getBin = getOutput " b i n " ;
2021-06-08 16:03:12 +02:00
/* G e t a p a c k a g e ' s ` l i b ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-24 14:08:11 +01:00
getLib pkgs . openssl
2021-06-08 16:03:12 +02:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - l i b "
Type :
2022-12-24 14:08:11 +01:00
getLib : : Derivation -> String
2021-06-08 16:03:12 +02:00
* /
2016-04-14 18:00:39 +02:00
getLib = getOutput " l i b " ;
2021-06-08 16:03:12 +02:00
/* G e t a p a c k a g e ' s ` d e v ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-24 14:08:11 +01:00
getDev pkgs . openssl
2021-06-08 16:03:12 +02:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - d e v "
Type :
2022-12-24 14:08:11 +01:00
getDev : : Derivation -> String
2021-06-08 16:03:12 +02:00
* /
2016-04-14 18:00:39 +02:00
getDev = getOutput " d e v " ;
2021-06-08 16:03:12 +02:00
/* G e t a p a c k a g e ' s ` m a n ` o u t p u t .
If the output does not exist , fallback to ` . out ` and then to the default .
Example :
2022-12-24 14:08:11 +01:00
getMan pkgs . openssl
2021-06-08 16:03:12 +02:00
= > " / n i x / s t o r e / 9 r z 8 g x h z f 8 s w 4 k f 2 j 2 f 1 g r r 4 9 w 8 z x 5 v j - o p e n s s l - 1 . 0 . 1 r - m a n "
Type :
2022-12-24 14:08:11 +01:00
getMan : : Derivation -> String
2021-06-08 16:03:12 +02:00
* /
2020-05-11 11:08:29 +02:00
getMan = getOutput " m a n " ;
2016-04-14 18:00:39 +02:00
2022-12-24 14:08:11 +01:00
/* P i c k t h e o u t p u t s o f p a c k a g e s t o p l a c e i n ` b u i l d I n p u t s `
Type : chooseDevOutputs : : [ Derivation ] -> [ String ]
* /
2024-03-03 04:01:35 +01:00
chooseDevOutputs = builtins . map getDev ;
2016-02-28 22:49:42 +01:00
2019-09-11 12:57:38 +02:00
/* M a k e v a r i o u s N i x t o o l s c o n s i d e r t h e c o n t e n t s o f t h e r e s u l t i n g
attribute set when looking for what to build , find , etc .
2019-09-11 14:51:28 +02:00
This function only affects a single attribute set ; it does not
apply itself recursively for nested attribute sets .
2021-06-08 16:03:12 +02:00
Example :
{ pkgs ? import <nixpkgs> { } }:
{
myTools = pkgs . lib . recurseIntoAttrs {
inherit ( pkgs ) hello figlet ;
} ;
}
Type :
recurseIntoAttrs : : AttrSet -> AttrSet
2022-12-24 14:08:11 +01:00
2019-09-11 12:57:38 +02:00
* /
recurseIntoAttrs =
2021-06-08 16:03:12 +02:00
# An attribute set to scan for derivations.
attrs :
attrs // { recurseForDerivations = true ; } ;
2019-09-11 12:57:38 +02:00
2019-09-11 13:03:48 +02:00
/* U n d o t h e e f f e c t o f r e c u r s e I n t o A t t r s .
2021-06-08 16:03:12 +02:00
Type :
2022-12-24 14:08:11 +01:00
dontRecurseIntoAttrs : : AttrSet -> AttrSet
2019-09-11 13:03:48 +02:00
* /
dontRecurseIntoAttrs =
2021-06-08 16:03:12 +02:00
# An attribute set to not scan for derivations.
attrs :
attrs // { recurseForDerivations = false ; } ;
2019-09-11 13:03:48 +02:00
2022-09-06 01:11:50 +02:00
/* ` u n i o n O f D i s j o i n t x y ` i s e q u a l t o ` x / / y / / z ` w h e r e t h e
attrnames in ` z ` are the intersection of the attrnames in ` x ` and
2022-09-12 21:59:16 +02:00
` y ` , and all values ` assert ` with an error message . This
2022-12-24 14:08:11 +01:00
operator is commutative , unlike ( // ) .
Type : unionOfDisjoint : : AttrSet -> AttrSet -> AttrSet
* /
2022-09-06 01:11:50 +02:00
unionOfDisjoint = x : y :
2022-09-25 09:09:13 +02:00
let
intersection = builtins . intersectAttrs x y ;
collisions = lib . concatStringsSep " " ( builtins . attrNames intersection ) ;
mask = builtins . mapAttrs ( name : value : builtins . throw
" u n i o n O f D i s j o i n t : c o l l i s i o n o n ${ name } ; c o m p l e t e l i s t : ${ collisions } " )
intersection ;
in
( x // y ) // mask ;
2022-09-06 01:11:50 +02:00
2022-12-24 14:08:11 +01:00
# DEPRECATED
2024-02-03 16:13:04 +01:00
zipWithNames = warn
" l i b . z i p W i t h N a m e s i s a d e p r e c a t e d a l i a s o f l i b . z i p A t t r s W i t h N a m e s . " zipAttrsWithNames ;
2022-12-24 14:08:11 +01:00
# DEPRECATED
2024-02-03 16:13:04 +01:00
zip = warn
" l i b . z i p i s a d e p r e c a t e d a l i a s o f l i b . z i p A t t r s W i t h . " zipAttrsWith ;
2009-03-10 16:18:38 +01:00
}