diff options
Diffstat (limited to 'lib/partial.bzl')
-rw-r--r-- | lib/partial.bzl | 48 |
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, ) |