codyc4321
codyc4321

Reputation: 9672

Gather the param names from your function declaration

I have the following generic debugger:

def debugger(method):

    def dec(*args, **kwargs):
        print("\ndebugging '{name}':\n".format(name=method.__name__))
        FOUR_SPACES = " " * 4
        EIGHT_SPACES = " " * 8
        if args:
            print("{four}args:\n".format(four=FOUR_SPACES))
            for arg in args:
                print("%s%s" % (EIGHT_SPACES, arg))
        if kwargs:
            for key, value in kwargs.items():
                print("{four}{key}:\n{eight}{value}\n".format(
                    four=FOUR_SPACES,
                    key=key,
                    eight=EIGHT_SPACES,
                    value=value
                ))

        if args or kwargs:
            print("\n")

        result = method(*args, **kwargs)
        return result

    return dec

@debugger
def generate_minutes_in_timespan(start, end, cutoff_date=None):
    # cutoff_date is just example of kwarg
    minutes_delta = (end - start).seconds / 60
    datetimes = []
    delta_range = range(0, minutes_delta + 1)
    return [start + timedelta(minutes=i) for i in delta_range]

Debugger will do:

debugging 'generate_minutes_in_timespan':

    args:

        2017-08-31 17:19:00
        2017-09-01 12:05:00

this pleases me slightly, but notice I missed the true info.

What I want is:

debugging 'generate_minutes_in_timespan':

    args:

        start = 2017-08-31 17:19:00
        end = 2017-09-01 12:05:00

    *args:

        not allowed

    kwargs:

        cutoff_date = None

    *kwargs:

        not allowed

Those args have semantic meaning, and can be used as start and end...they aren't *args.

Is there a way deep in python standard library I can read this method generate_minutes_in_timespan and pluck off the declaration, the names and order of required positional args, also the names of any declared kwargs (like declaring cutoff_date=None, I would want to know cutoff_date was offered as a kwarg, vs the optional **kwargs...)

I shoot for {'args': [('start', 'the value given'), ('end', 'the user given value')], 'star_args': None, 'kwargs': {'cutoff_date': None}, 'star_kwargs': None} for full debug info.

Upvotes: 2

Views: 57

Answers (3)

codyc4321
codyc4321

Reputation: 9672

The result is nice:

import inspect

def debugger(method):

    def dec(*args, **kwargs):
        print("\ndebugging '{name}':\n".format(name=method.__name__))
        FOUR_SPACES = " " * 4
        EIGHT_SPACES = " " * 8

        inspector = inspect.getargspec(method)

        arg_names = inspector.args
        var_args = inspector.varargs
        keyword_args = inspector.keywords

        def wrap_string_in_quotes(value):
            if isinstance(value, str):
                return '"' + value + '"'
            else:
                return value

        if args:
            print("{four}args:\n".format(four=FOUR_SPACES))
            for index, arg_name in enumerate(arg_names):
                print(
                    "{eight}{name} = {arg}" \
                    .format(
                        eight=EIGHT_SPACES,
                        name=arg_name,
                        arg=wrap_string_in_quotes(args[index])
                    )
                )

        if var_args:
            start = len(arg_names)
            these_args = args[start:]
            print("\n{four}*{star_arg_name}:\n".format(four=FOUR_SPACES, star_arg_name=var_args))
            for this_var_arg in these_args:
                print("{eight}{arg}".format(eight=EIGHT_SPACES, arg=wrap_string_in_quotes(this_var_arg)))

        if kwargs:
            print("\n{four}**{kwargs_name}:\n".format(four=FOUR_SPACES, kwargs_name=keyword_args))
            for key, value in kwargs.items():
                print("{eight}{key} = {value}\n".format(
                    eight=EIGHT_SPACES,
                    key=key,
                    value=wrap_string_in_quotes(value)
                ))

        if args or kwargs:
            print("\n")

        result = method(*args, **kwargs)
        return result

    return dec


@debugger
def one_two_kwarg_three_star_args_star_kwargs(one, two, three=None,  *args, **kwargs):
    pass

one_two_kwarg_three_star_args_star_kwargs(1, 2, 3, 'star arg 1', 'star arg 2', blah='blah, kwarg')

does

$ python debugger.py 

debugging 'one_two_kwarg_three_star_args_star_kwargs':

    args:

        one = 1
        two = 2
        three = 3

    *args:

        "star arg 1"
        "star arg 2"

    **kwargs:

        blah = "blah, kwarg"

Upvotes: 1

user2357112
user2357112

Reputation: 280301

Use inspect.signature(method).bind:

for name, val in inspect.signature(method).bind(*args, **kwargs).arguments:
    print('{} = {}'.format(name, val))

If you're on Python 2, there's a backport of this functionality on PyPI you can use.

Upvotes: 3

wim
wim

Reputation: 362557

From within the decorator, use the inspect module to discover the arg names:

>>> def generate_minutes_in_timespan(start, end, cutoff_date=None):
...     ...
... 
>>> inspect.getargspec(generate_minutes_in_timespan)
ArgSpec(args=['start', 'end', 'cutoff_date'], varargs=None, keywords=None, defaults=(None,))
               ^        ^
               |________|_____ here they are...

Matching up the arguments passed with the actual arg names is left as exercise for the OP.

Upvotes: 4

Related Questions