]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
pybind/mgr: enhance CLICommand to fetch extra args from wrapped funcs
authorJohn Mulligan <jmulligan@redhat.com>
Mon, 11 Apr 2022 19:03:12 +0000 (15:03 -0400)
committerJohn Mulligan <jmulligan@redhat.com>
Fri, 13 May 2022 14:42:00 +0000 (10:42 -0400)
Previously, the CLICommand decorator "assumed" that the decorator was
applied directly to a mgr module api endpoint function. Now that we plan
on adding the Responder decorator into the mix we need a way of
properly fetching the arguments of the endpoint function. In addition,
the decorator itself needs to provide extra arguments to the mgr
(in cases where the endpoint function doesn't explicitly ask for it).
Thus we add a helper function to find the endpoint function when
wrapped as well as extract extra arguments when "walking" the stack
of __wrapped__ functions.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
src/pybind/mgr/mgr_module.py

index 1aa50b049d3f9b4c8038dbd0cc8161c0a179297f..a45282283217c064f97c19f841ccb17966d9da89 100644 (file)
@@ -329,6 +329,26 @@ class CRUSHMap(ceph_module.BasePyCRUSH):
 
 HandlerFuncType = Callable[..., Tuple[int, str, str]]
 
+def _extract_target_func(
+    f: HandlerFuncType
+) -> Tuple[HandlerFuncType, Dict[str, Any]]:
+    """In order to interoperate with other decorated functions,
+    we need to find the original function which will provide
+    the main set of arguments. While we descend through the
+    stack of wrapped functions, gather additional arguments
+    the decorators may want to provide.
+    """
+    # use getattr to keep mypy happy
+    wrapped = getattr(f, "__wrapped__", None)
+    if not wrapped:
+        return f, {}
+    extra_args = {}
+    while wrapped is not None:
+        extra_args.update(getattr(f, "extra_args", {}))
+        f = wrapped
+        wrapped = getattr(f, "__wrapped__", None)
+    return f, extra_args
+
 
 class CLICommand(object):
     COMMANDS = {}  # type: Dict[str, CLICommand]
@@ -348,6 +368,7 @@ class CLICommand(object):
 
     @classmethod
     def _load_func_metadata(cls: Any, f: HandlerFuncType) -> Tuple[str, Dict[str, Any], int, str]:
+        f, extra_args = _extract_target_func(f)
         desc = (inspect.getdoc(f) or '').replace('\n', ' ')
         full_argspec = inspect.getfullargspec(f)
         arg_spec = full_argspec.annotations
@@ -377,6 +398,14 @@ class CLICommand(object):
                                                dict(name=arg),
                                                has_default,
                                                positional))
+        for argname, argtype in extra_args.items():
+            # avoid shadowing args from the function
+            if argname in arg_spec:
+                continue
+            arg_spec[argname] = argtype
+            args.append(CephArgtype.to_argdesc(
+                argtype, dict(name=arg), has_default=True, positional=False
+            ))
         return desc, arg_spec, first_default, ' '.join(args)
 
     def store_func_metadata(self, f: HandlerFuncType) -> None: