Skip to content

Commit 7216be3

Browse files
committed
cleanup
1 parent 4189af4 commit 7216be3

File tree

2 files changed

+196
-101
lines changed

2 files changed

+196
-101
lines changed

python/private/builders.bzl

Lines changed: 191 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,145 @@ def _DepsetBuilder_build(self):
9696
kwargs["order"] = self._order[0]
9797
return depset(direct = self.direct, transitive = self.transitive, **kwargs)
9898

99+
def _Optional(*initial):
100+
"""A wrapper for a re-assignable value that may or may not be set.
101+
102+
This allows structs to have attributes that aren't inherently mutable
103+
and must be re-assigned to have their value updated.
104+
105+
Args:
106+
*initial: A single vararg to be the initial value, or no args
107+
to leave it unset.
108+
109+
Returns:
110+
{type}`Optional`
111+
"""
112+
if len(initial) > 1:
113+
fail("Only zero or one positional arg allowed")
114+
115+
# buildifier: disable=uninitialized
116+
self = struct(
117+
_value = list(initial),
118+
present = lambda *a, **k: _Optional_present(self, *a, **k),
119+
set = lambda *a, **k: _Optional_set(self, *a, **k),
120+
get = lambda *a, **k: _Optional_get(self, *a, **k),
121+
)
122+
return self
123+
124+
def _Optional_set(self, value):
125+
"""Sets the value of the optional.
126+
127+
Args:
128+
self: implicitly added
129+
value: the value to set.
130+
"""
131+
if len(self._value) == 0:
132+
self._value.append(value)
133+
else:
134+
self._value[0] = value
135+
136+
def _Optional_get(self):
137+
"""Gets the value of the optional, or error.
138+
139+
Args:
140+
self: implicitly added
141+
142+
Returns:
143+
The stored value, or error if not set.
144+
"""
145+
if not len(self._value):
146+
fail("Value not present")
147+
return self._value[0]
148+
149+
def _Optional_present(self):
150+
"""Tells if a value is present.
151+
152+
Args:
153+
self: implicitly added
154+
155+
Returns:
156+
{type}`bool` True if the value is set, False if not.
157+
"""
158+
return len(self._value) > 0
159+
160+
def _RuleBuilder(implementation = None, **kwargs):
161+
"""Builder for creating rules.
162+
163+
Args:
164+
implementation: {type}`callable` The rule implementation function.
165+
**kwargs: The same as the `rule()` function, but using builders
166+
for the non-mutable Bazel objects.
167+
"""
168+
169+
# buildifier: disable=uninitialized
170+
self = struct(
171+
attrs = dict(kwargs.pop("attrs", None) or {}),
172+
cfg = kwargs.pop("cfg", None) or _TransitionBuilder(),
173+
exec_groups = dict(kwargs.pop("exec_groups", None) or {}),
174+
executable = _Optional(),
175+
fragments = list(kwargs.pop("fragments", None) or []),
176+
implementation = _Optional(implementation),
177+
extra_kwargs = kwargs,
178+
provides = list(kwargs.pop("provides", None) or []),
179+
test = _Optional(),
180+
toolchains = list(kwargs.pop("toolchains", None) or []),
181+
build = lambda *a, **k: _RuleBuilder_build(self, *a, **k),
182+
to_kwargs = lambda *a, **k: _RuleBuilder_to_kwargs(self, *a, **k),
183+
)
184+
if "test" in kwargs:
185+
self.test.set(kwargs.pop("test"))
186+
if "executable" in kwargs:
187+
self.executable.set(kwargs.pop("executable"))
188+
return self
189+
190+
def _RuleBuilder_build(self, debug = ""):
191+
"""Builds a `rule` object
192+
193+
Args:
194+
self: implicitly added
195+
debug: {type}`str` If set, prints the args used to create the rule.
196+
197+
Returns:
198+
{type}`rule`
199+
"""
200+
kwargs = self.to_kwargs()
201+
if debug:
202+
lines = ["=" * 80, "rule kwargs: {}:".format(debug)]
203+
for k, v in sorted(kwargs.items()):
204+
lines.append(" {}={}".format(k, v))
205+
print("\n".join(lines)) # buildifier: disable=print
206+
return rule(**kwargs)
207+
208+
def _RuleBuilder_to_kwargs(self):
209+
"""Builds the arguments for calling `rule()`.
210+
211+
Args:
212+
self: implicitly added
213+
214+
Returns:
215+
{type}`dict`
216+
"""
217+
kwargs = {}
218+
if self.executable.present():
219+
kwargs["executable"] = self.executable.get()
220+
if self.test.present():
221+
kwargs["test"] = self.test.get()
222+
223+
kwargs.update(
224+
implementation = self.implementation.get(),
225+
cfg = self.cfg.build() if self.cfg.implementation.present() else None,
226+
attrs = {
227+
k: (v.build() if hasattr(v, "build") else v)
228+
for k, v in self.attrs.items()
229+
},
230+
exec_groups = self.exec_groups,
231+
fragments = self.fragments,
232+
provides = self.provides,
233+
toolchains = self.toolchains,
234+
)
235+
kwargs.update(self.extra_kwargs)
236+
return kwargs
237+
99238
def _RunfilesBuilder():
100239
"""Creates a `RunfilesBuilder`.
101240
@@ -177,41 +316,60 @@ def _RunfilesBuilder_build(self, ctx, **kwargs):
177316
**kwargs
178317
).merge_all(self.runfiles)
179318

180-
# Skylib's types module doesn't have is_file, so roll our own
181-
def _is_file(value):
182-
return type(value) == "File"
319+
def _SetBuilder(initial = None):
320+
"""Builder for list of unique values.
183321
184-
def _is_runfiles(value):
185-
return type(value) == "runfiles"
322+
Args:
323+
initial: {type}`list | None` The initial values.
186324
187-
def _Optional(*initial):
188-
if len(initial) > 1:
189-
fail("only one positional arg allowed")
325+
Returns:
326+
{type}`SetBuilder`
327+
"""
328+
initial = {} if not initial else {v: None for v in initial}
190329

191330
# buildifier: disable=uninitialized
192331
self = struct(
193-
_value = list(initial),
194-
present = lambda *a, **k: _Optional_present(self, *a, **k),
195-
set = lambda *a, **k: _Optional_set(self, *a, **k),
196-
get = lambda *a, **k: _Optional_get(self, *a, **k),
332+
# TODO - Switch this to use set() builtin when available
333+
# https://bazel.build/rules/lib/core/set
334+
_values = initial,
335+
update = lambda *a, **k: _SetBuilder_update(self, *a, **k),
336+
build = lambda *a, **k: _SetBuilder_build(self, *a, **k),
197337
)
198338
return self
199339

200-
def _Optional_set(self, v):
201-
if len(self._value) == 0:
202-
self._value.append(v)
203-
else:
204-
self._value[0] = v
340+
def _SetBuilder_build(self):
341+
"""Builds the values into a list
205342
206-
def _Optional_get(self):
207-
if not len(self._value):
208-
fail("Value not present")
209-
return self._value[0]
343+
Returns:
344+
{type}`list`
345+
"""
346+
return self._values.keys()
210347

211-
def _Optional_present(self):
212-
return len(self._value) > 0
348+
def _SetBuilder_update(self, *others):
349+
"""Adds values to the builder.
350+
351+
Args:
352+
self: implicitly added
353+
*others: {type}`list` values to add to the set.
354+
"""
355+
for other in others:
356+
for value in other:
357+
if value not in self._values:
358+
self._values[value] = None
213359

214360
def _TransitionBuilder(implementation = None, inputs = None, outputs = None, **kwargs):
361+
"""Builder for transition objects.
362+
363+
Args:
364+
implementation: {type}`callable` the transition implementation function.
365+
inputs: {type}`list[str]` the inputs for the transition.
366+
outputs: {type}`list[str]` the outputs of the transition.
367+
**kwargs: Extra keyword args to use when building.
368+
369+
Returns:
370+
{type}`TransitionBuilder`
371+
"""
372+
215373
# buildifier: disable=uninitialized
216374
self = struct(
217375
implementation = _Optional(implementation),
@@ -231,87 +389,24 @@ def _TransitionBuilder(implementation = None, inputs = None, outputs = None, **k
231389
return self
232390

233391
def _TransitionBuilder_build(self):
392+
"""Creates a transition from the builder.
393+
394+
Returns:
395+
{type}`transition`
396+
"""
234397
return transition(
235398
implementation = self.implementation.get(),
236399
inputs = self.inputs.build(),
237400
outputs = self.outputs.build(),
238401
**self.extra_kwargs
239402
)
240403

241-
def _SetBuilder(initial = None):
242-
initial = {} if not initial else {v: None for v in initial}
243-
244-
# buildifier: disable=uninitialized
245-
self = struct(
246-
# TODO - Switch this to use set() builtin when available
247-
# https://bazel.build/rules/lib/core/set
248-
_values = initial,
249-
update = lambda *a, **k: _SetBuilder_update(self, *a, **k),
250-
build = lambda *a, **k: _SetBuilder_build(self, *a, **k),
251-
)
252-
return self
253-
254-
def _SetBuilder_build(self):
255-
return self._values.keys()
256-
257-
def _SetBuilder_update(self, *others):
258-
for other in others:
259-
for value in other:
260-
if value not in self._values:
261-
self._values[value] = None
262-
263-
def _RuleBuilder(implementation = None, **kwargs):
264-
# buildifier: disable=uninitialized
265-
self = struct(
266-
attrs = dict(kwargs.pop("attrs", None) or {}),
267-
cfg = kwargs.pop("cfg", None) or _TransitionBuilder(),
268-
exec_groups = dict(kwargs.pop("exec_groups", None) or {}),
269-
executable = _Optional(),
270-
fragments = list(kwargs.pop("fragments", None) or []),
271-
implementation = _Optional(implementation),
272-
extra_kwargs = kwargs,
273-
provides = list(kwargs.pop("provides", None) or []),
274-
test = _Optional(),
275-
toolchains = list(kwargs.pop("toolchains", None) or []),
276-
build = lambda *a, **k: _RuleBuilder_build(self, *a, **k),
277-
to_kwargs = lambda *a, **k: _RuleBuilder_to_kwargs(self, *a, **k),
278-
)
279-
if "test" in kwargs:
280-
self.test.set(kwargs.pop("test"))
281-
if "executable" in kwargs:
282-
self.executable.set(kwargs.pop("executable"))
283-
return self
284-
285-
def _RuleBuilder_build(self, debug = ""):
286-
kwargs = self.to_kwargs()
287-
if debug:
288-
lines = ["=" * 80, "rule kwargs: {}:".format(debug)]
289-
for k, v in sorted(kwargs.items()):
290-
lines.append(" {}={}".format(k, v))
291-
print("\n".join(lines)) # buildifier: disable=print
292-
return rule(**kwargs)
293-
294-
def _RuleBuilder_to_kwargs(self):
295-
kwargs = {}
296-
if self.executable.present():
297-
kwargs["executable"] = self.executable.get()
298-
if self.test.present():
299-
kwargs["test"] = self.test.get()
404+
# Skylib's types module doesn't have is_file, so roll our own
405+
def _is_file(value):
406+
return type(value) == "File"
300407

301-
kwargs.update(
302-
implementation = self.implementation.get(),
303-
cfg = self.cfg.build(),
304-
attrs = {
305-
k: (v.build() if hasattr(v, "build") else v)
306-
for k, v in self.attrs.items()
307-
},
308-
exec_groups = self.exec_groups,
309-
fragments = self.fragments,
310-
provides = self.provides,
311-
toolchains = self.toolchains,
312-
)
313-
kwargs.update(self.extra_kwargs)
314-
return kwargs
408+
def _is_runfiles(value):
409+
return type(value) == "runfiles"
315410

316411
builders = struct(
317412
DepsetBuilder = _DepsetBuilder,

tests/toolchains/python_toolchain_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import json
22
import os
33
import pathlib
4+
import pprint
45
import sys
56
import unittest
6-
import pprint
77

88
from python.runfiles import runfiles
99

@@ -20,10 +20,10 @@ def test_expected_toolchain_matches(self):
2020

2121
expected = "python_{}".format(expect_version.replace(".", "_"))
2222
msg = (
23-
"Expected toolchain not found\n" +
24-
f"Expected toolchain label to contain: {expected}\n" +
25-
"Actual build settings:\n" +
26-
pprint.pformat(settings)
23+
"Expected toolchain not found\n"
24+
+ f"Expected toolchain label to contain: {expected}\n"
25+
+ "Actual build settings:\n"
26+
+ pprint.pformat(settings)
2727
)
2828
self.assertIn(expected, settings["toolchain_label"], msg)
2929

0 commit comments

Comments
 (0)