aboutsummaryrefslogtreecommitdiff
path: root/lib/partial.bzl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/partial.bzl')
-rw-r--r--lib/partial.bzl48
1 files changed, 47 insertions, 1 deletions
diff --git a/lib/partial.bzl b/lib/partial.bzl
index e2f24b7..a642d96 100644
--- a/lib/partial.bzl
+++ b/lib/partial.bzl
@@ -19,6 +19,11 @@ Partial function objects allow some parameters are bound before the call.
Similar to https://docs.python.org/3/library/functools.html#functools.partial.
"""
+# create instance singletons to avoid unnecessary allocations
+_a_dict_type = type({})
+_a_tuple_type = type(())
+_a_struct_type = type(struct())
+
def _call(partial, *args, **kwargs):
"""Calls a partial created using `make`.
@@ -46,16 +51,22 @@ def _make(func, *args, **kwargs):
A partial 'function' can be defined with positional args and kwargs:
# function with no args
+ ```
def function1():
...
+ ```
# function with 2 args
+ ```
def function2(arg1, arg2):
...
+ ```
# function with 2 args and keyword args
+ ```
def function3(arg1, arg2, x, y):
...
+ ```
The positional args passed to the function are the args passed into make
followed by any additional positional args given to call. The below example
@@ -63,24 +74,30 @@ def _make(func, *args, **kwargs):
make and the other by call:
# function demonstrating 1 arg at make site, and 1 arg at call site
+ ```
def _foo(make_arg1, func_arg1):
- print(make_arg1 + " " + func_arg1 + "!")
+ print(make_arg1 + " " + func_arg1 + "!")
+ ```
For example:
+ ```
hi_func = partial.make(_foo, "Hello")
bye_func = partial.make(_foo, "Goodbye")
partial.call(hi_func, "Jennifer")
partial.call(hi_func, "Dave")
partial.call(bye_func, "Jennifer")
partial.call(bye_func, "Dave")
+ ```
prints:
+ ```
"Hello, Jennifer!"
"Hello, Dave!"
"Goodbye, Jennifer!"
"Goodbye, Dave!"
+ ```
The keyword args given to the function are the kwargs passed into make
unioned with the keyword args given to call. In case of a conflict, the
@@ -90,16 +107,20 @@ def _make(func, *args, **kwargs):
Example with a make site arg, a call site arg, a make site kwarg and a
call site kwarg:
+ ```
def _foo(make_arg1, call_arg1, make_location, call_location):
print(make_arg1 + " is from " + make_location + " and " +
call_arg1 + " is from " + call_location + "!")
func = partial.make(_foo, "Ben", make_location="Hollywood")
partial.call(func, "Jennifer", call_location="Denver")
+ ```
Prints "Ben is from Hollywood and Jennifer is from Denver!".
+ ```
partial.call(func, "Jennifer", make_location="LA", call_location="Denver")
+ ```
Prints "Ben is from LA and Jennifer is from Denver!".
@@ -107,11 +128,13 @@ def _make(func, *args, **kwargs):
whether they are given during the make or call step. For instance, you can't
do:
+ ```
def foo(x):
pass
func = partial.make(foo, 1)
partial.call(func, x=2)
+ ```
Args:
func: The function to be called.
@@ -124,7 +147,30 @@ def _make(func, *args, **kwargs):
"""
return struct(function = func, args = args, kwargs = kwargs)
+def _is_instance(v):
+ """Returns True if v is a partial created using `make`.
+
+ Args:
+ v: The value to check.
+
+ Returns:
+ True if v was created by `make`, False otherwise.
+ """
+
+ # Note that in bazel 3.7.0 and earlier, type(v.function) is the same
+ # as the type of a function even if v.function is a rule. But we
+ # cannot rely on this in later bazels due to breaking change
+ # https://github.com/bazelbuild/bazel/commit/e379ece1908aafc852f9227175dd3283312b4b82
+ #
+ # Since this check is heuristic anyway, we simply check for the
+ # presence of a "function" attribute without checking its type.
+ return type(v) == _a_struct_type and \
+ hasattr(v, "function") and \
+ hasattr(v, "args") and type(v.args) == _a_tuple_type and \
+ hasattr(v, "kwargs") and type(v.kwargs) == _a_dict_type
+
partial = struct(
make = _make,
call = _call,
+ is_instance = _is_instance,
)