Search code examples
pythonrepr

How to set a repr for a function itself?


__repr__ is used to return a string representation of an object, but in Python a function is also an object itself, and can have attributes.

How do I set the __repr__ of a function?

I see here that an attribute can be set for a function outside the function, but typically one sets a __repr__ within the object definition itself, so I'd like to set the repr within the function definition itself.


My use case is that I am using tenacity to retry a networking function with exponential backoff, and I want to log the (informative) name of the function I have called last.

retry_mysql_exception_types = (InterfaceError, OperationalError, TimeoutError, ConnectionResetError)


def return_last_retry_outcome(retry_state):
    """return the result of the last call attempt"""
    return retry_state.outcome.result()


def my_before_sleep(retry_state):
    print("Retrying {}: attempt {} ended with: {}\n".format(retry_state.fn, retry_state.attempt_number, retry_state.outcome))


@tenacity.retry(wait=tenacity.wait_random_exponential(multiplier=1, max=1200),
                stop=tenacity.stop_after_attempt(30),
                retry=tenacity.retry_if_exception_type(retry_mysql_exception_types),
                retry_error_callback=return_last_retry_outcome,
                before_sleep=my_before_sleep)
def connect_with_retries(my_database_config):
    connection = mysql.connector.connect(**my_database_config)
    return connection

Currently retry_state.fn displays something like <function <lambda> at 0x1100f6ee0> like @chepner says, but I'd like to add more information to it.


Solution

  • I think a custom decorator could help:

    import functools
    
    
    class reprable:
        """Decorates a function with a repr method.
    
        Example:
            >>> @reprable
            ... def foo():
            ...     '''Does something cool.'''
            ...     return 4
            ...
            >>> foo()
            4
            >>> foo.__name__
            'foo'
            >>> foo.__doc__
            'Does something cool.'
            >>> repr(foo)
            'foo: Does something cool.'
            >>> type(foo)
            <class '__main__.reprable'>
        """
    
        def __init__(self, wrapped):
            self._wrapped = wrapped
            functools.update_wrapper(self, wrapped)
    
        def __call__(self, *args, **kwargs):
            return self._wrapped(*args, **kwargs)
    
        def __repr__(self):
            return f'{self._wrapped.__name__}: {self._wrapped.__doc__}'
    

    Demo: http://tpcg.io/uTbSDepz.