Decorator to return default argument values I'm using Python 3.6. This code, used as a decorator to a python function, will cause the function to return a dict of POSITION_OR_KEYWORD arguments.

I used this to create a standard interface to a factory function. 

Inspired by this Stack Overflow post, and implemented using a dictionary-flattening function from this previous post.

Eg. 

def example(one=1, two=2):
    return {'one':one, 'two':two}


is equivalent to:

@return_kwargs
def example(one=1, two=2):
    pass




import inspect
from functools import wraps
from itertools import chain, starmap

def flatten_dict(dictionary):
    """Flatten a nested dictionary structure"""

    def unpack(parent_key, parent_value):
        """Unpack one level of nesting in a dictionary"""
        try:
            items = parent_value.items()
        except AttributeError:
            # parent_value was not a dict, no need to flatten
            yield (parent_key, parent_value)
        else:
            for key, value in items:
                yield (key, value)

    # Put each key into a tuple to initiate building a tuple of subkeys
    dictionary = {key: value for key, value in dictionary.items()}

    # Keep unpacking the dictionary until all value's are not dictionary's
    dictionary = dict(chain.from_iterable(starmap(unpack, dictionary.items())))
    return dictionary

def return_kwargs(func):
    """Cause the wrapped function to return a dict of keyword arguments"""
    @wraps(func)
    def wrap(*args, **kwargs):
        bound_arguments = inspect.signature(func).bind(*args, **kwargs)
        bound_arguments.apply_defaults()
        return flatten_dict(dict(bound_arguments.arguments))
    return wrap


Let me know how you would implement the idea, whats ugly, corner cases I have missed.
I am not sure how critical this use case is, but when any of your arguments have a dictionary value, you have that dictionary items used as keyword arguments for the function:

In [1]: @return_kwargs
   ...: def example(arg1):
   ...:     pass
   ...: 

In [2]: arg1_value = {'arg3': 'value'}

In [3]: print(example(arg1_value))
{'arg3': 'value'}


In other words, arg3 here is printed as a keyword argument for the function but it was not actually unpacked into keyword arguments to the function at all - it was "sitting" inside the value of an arg1 positional argument.