Search code examples
python-3.xsignaturekeyword-argument

Get name of kwargs from method signature to flatten a method signature into a single dict using locals()


I have a class method of the form:

def _cool_method(arg1, arg2, **kwargs):
  <do pre-processing stuff>
  super()._cool_method(**flattened_kwargs)

where I want flattened_kwargs to be a dict like:

flattened_kwargs = {
    "arg1": value,
    "arg2" value,
    "kwargs_key1": value,
    "kwargs_key2": {"nestedarg1": value, "nestedarg2": value}
    "kwargs_key3": value,
}

I need to pass a single kwargs dict to the super call after some processing in the child method.

I have managed to figure out how to flatten the input signature into a single dict using locals():

attrs = locals().copy()
attrs.pop("kwargs")
attrs.update(kwargs)

I would now like to convert this to a helper function to call in a number of child class methods. However, the above code assumes that I know the name of **kwargs as defined by the method signature. I would like to inspect this from the child method so that I don't need to hardcode an assumed key into the pop() call.

I understand that in Python 3.6 I should be using inspect and have managed to get this far:

def _get_signature_kwargs_key(f):
    keys = [k for k, v in signature(f).parameters.items() if v.kind == v.VAR_KEYWORD]
    return keys[0] if len(keys) == 1 else None


def flatten_signature_args(f, loc):
    kwargs_name = _get_signature_kwargs_key(f)
    attributes = loc.copy()
    # cater for method calls from classes, which include unwanted metadata
    for meta in ['__class__', 'self']:
        try:
            del attributes[meta]
        except KeyError:
            pass
    attributes.pop(kwargs_name)
    attributes.update(loc[kwargs_name])
    return attributes

For obvious reasons, the assumption of VAR_KEYWORD uniqueness appears reasonable. However, is there a better approach to extracting this from the signature, or perhaps a better approach to my requirement for flattening the overall signature?

I'm also having to do a hack to remove meta keys when calling a class method, or it fails when I pass self._cool_method as the input function.


Solution

  • Solved with:

    def get_signature_locals(f, loc):]
        return {k: v for k, v in loc.items() if k in signature(f).parameters}
    
    def get_signature_kwargs_key(f):
        keys = [k for k, v in signature(f).parameters.items() if v.kind == v.VAR_KEYWORD]
        try:
            return keys.pop()
        except IndexError:
            return None
    
    def flatten_signature_kwargs(func, loc):
        kwargs_name = get_signature_kwargs_key(func)
        attributes = get_signature_locals(func, loc)
        if kwargs_name:
            attributes.pop(kwargs_name)
            attributes.update(loc[kwargs_name])
        return attributes