23
23
]
24
24
25
25
26
+ def overlay_dict (base : dict , update : dict ):
27
+ """Overlay a dict over a base one"""
28
+ base = base .copy ()
29
+ for key , val in update .items ():
30
+ if key in base and isinstance (val , dict ):
31
+ base [key ] = overlay_dict (base .get (key , {}), val )
32
+ else :
33
+ base [key ] = val
34
+ return base
35
+
36
+
26
37
def field_fmt (field , args ):
27
38
"""If `field` is a string, interpolate variables in `args`"""
28
39
if not isinstance (field , str ):
29
40
return field
30
41
return field .format (** args )
31
42
32
43
33
- def group (label , command , instances , platforms , agent_tags = None , ** kwargs ):
44
+ def dict_fmt (dict_tmpl , args ):
45
+ """Apply field_fmt over a hole dict"""
46
+ res = {}
47
+ for key , val in dict_tmpl .items ():
48
+ if isinstance (val , dict ):
49
+ res [key ] = dict_fmt (val , args )
50
+ else :
51
+ res [key ] = field_fmt (val , args )
52
+ return res
53
+
54
+
55
+ def group (label , command , instances , platforms , ** kwargs ):
34
56
"""
35
57
Generate a group step with specified parameters, for each instance+kernel
36
58
combination
37
59
38
60
https://buildkite.com/docs/pipelines/group-step
39
61
"""
40
- if agent_tags is None :
41
- agent_tags = []
42
62
# Use the 1st character of the group name (should be an emoji)
43
63
label1 = label [0 ]
44
64
steps = []
@@ -48,16 +68,14 @@ def group(label, command, instances, platforms, agent_tags=None, **kwargs):
48
68
for instance in instances :
49
69
for os , kv in platforms :
50
70
# fill any templated variables
51
- args = {"os" : os , "kv" : kv , "instance" : instance }
52
- step_commands = [cmd .format (** args ) for cmd in commands ]
53
- step_kwargs = {key : field_fmt (val , args ) for key , val in kwargs .items ()}
54
- agents = [f"instance={ instance } " , f"kv={ kv } " , f"os={ os } " ] + agent_tags
71
+ args = {"instance" : instance , "os" : os , "kv" : kv }
55
72
step = {
56
- "command" : step_commands ,
73
+ "command" : [ cmd . format ( ** args ) for cmd in commands ] ,
57
74
"label" : f"{ label1 } { instance } { os } { kv } " ,
58
- "agents" : agents ,
59
- ** step_kwargs ,
75
+ "agents" : args ,
60
76
}
77
+ step_kwargs = dict_fmt (kwargs , args )
78
+ step = overlay_dict (step_kwargs , step )
61
79
steps .append (step )
62
80
63
81
return {"group" : label , "steps" : steps }
@@ -68,6 +86,31 @@ def pipeline_to_json(pipeline):
68
86
return json .dumps (pipeline , indent = 4 , sort_keys = True , ensure_ascii = False )
69
87
70
88
89
+ class DictAction (argparse .Action ):
90
+ """An argparse action that can receive a nested dictionary
91
+
92
+ Examples:
93
+
94
+ --step-param a/b/c=3
95
+ {"a": {"b": {"c": 3}}}
96
+ """
97
+
98
+ def __init__ (self , option_strings , dest , nargs = None , ** kwargs ):
99
+ if nargs is not None :
100
+ raise ValueError ("nargs not allowed" )
101
+ super ().__init__ (option_strings , dest , ** kwargs )
102
+
103
+ def __call__ (self , parser , namespace , value , option_string = None ):
104
+ res = getattr (namespace , self .dest , {})
105
+ key_str , val = value .split ("=" , maxsplit = 1 )
106
+ keys = key_str .split ("/" )
107
+ update = {keys [- 1 ]: val }
108
+ for key in list (reversed (keys ))[1 :]:
109
+ update = {key : update }
110
+ res = overlay_dict (res , update )
111
+ setattr (namespace , self .dest , res )
112
+
113
+
71
114
COMMON_PARSER = argparse .ArgumentParser ()
72
115
COMMON_PARSER .add_argument (
73
116
"--instances" ,
@@ -88,16 +131,7 @@ def pipeline_to_json(pipeline):
88
131
metavar = "PARAM=VALUE" ,
89
132
help = "parameters to add to each step" ,
90
133
required = False ,
91
- action = "append" ,
92
- default = [],
93
- type = lambda arg : tuple (arg .split ("=" , maxsplit = 1 )),
94
- )
95
- COMMON_PARSER .add_argument (
96
- "--step-env" ,
97
- metavar = "KEY=VALUE" ,
98
- help = "environment to use in each step" ,
99
- required = False ,
100
- action = "append" ,
101
- default = [],
102
- type = lambda arg : tuple (arg .split ("=" , maxsplit = 1 )),
134
+ action = DictAction ,
135
+ default = {},
136
+ type = str ,
103
137
)
0 commit comments