Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow help() to work on partial function object

Tags:

python

pydoc

I'm trying to make sure running help() at the Python 2.7 REPL displays the __doc__ for a function that was wrapped with functools.partial. Currently running help() on a functools.partial 'function' displays the __doc__ of the functools.partial class, not my wrapped function's __doc__. Is there a way to achieve this?

Consider the following callables:

def foo(a):
    """My function"""
    pass

partial_foo = functools.partial(foo, 2)

Running help(foo) will result in showing foo.__doc__. However, running help(partial_foo) results in the __doc__ of a Partial object.

My first approach was to use functools.update_wrapper which correctly replaces the partial object's __doc__ with foo.__doc__. However, this doesn't fix the 'problem' because of how pydoc.

I've investigated the pydoc code, and the issue seems to be that partial_foo is actually a Partial object not a typical function/callable, see this question for more information on that detail.

By default, pydoc will display the __doc__ of the object type, not instance if the object it was passed is determined to be a class by inspect.isclass. See the render_doc function for more information about the code itself.

So, in my scenario above pydoc is displaying the help of the type, functools.partial NOT the __doc__ of my functools.partial instance.

Is there anyway to make alter my call to help() or functools.partial instance that's passed to help() so that it will display the __doc__ of the instance, not type?

like image 202
durden2.0 Avatar asked May 21 '13 14:05

durden2.0


People also ask

What is partial () in python?

What is a Partial Function? Using partial functions is a component of metaprogramming in Python, a concept that refers to a programmer writing code that manipulates code. You can think of a partial function as an extension of another specified function.

How do you do partial functions?

Partial functions allow us to fix a certain number of arguments of a function and generate a new function. # a as 3, b as 1 and c as 4. In the example we have pre-filled our function with some constant values of a, b and c. And g() just takes a single argument i.e. the variable x.

How do you create a partial function in python?

You can create partial functions in python by using the partial function from the functools library. Partial functions allow one to derive a function with x parameters to a function with fewer parameters and fixed values set for the more limited function. This code will return 8.

What is partial function give its expression?

In mathematics, a partial function f from a set X to a set Y is a function from a subset S of X (possibly X itself) to Y. The subset S, that is, the domain of f viewed as a function, is called the domain of definition of f. If S equals X, that is, if f is defined on every element in X, then f is said to be total.


1 Answers

I found a pretty hacky way to do this. I wrote the following function to override the __builtins__.help function:

def partialhelper(object=None):
    if isinstance(object, functools.partial):
        return pydoc.help(object.func)
    else:
        # Preserve the ability to go into interactive help if user calls
        # help() with no arguments.
        if object is None:
            return pydoc.help()
        else:
            return pydoc.help(object)

Then just replace it in the REPL with:

__builtins__.help = partialhelper

This works and doesn't seem to have any major downsides, yet. However, there isn't a way with the above naive implementation to support still showing the __doc__ of some functools.partial objects. It's all or nothing, but could probably attach an attribute to the wrapped (original) function to indicate whether or not the original __doc__ should be shown. However, in my scenario I never want to do this.

Note the above does NOT work when using IPython and the embed functionality. This is because IPython directly sets the shell's namespace with references to the 'real' __builtin__, see the code and old mailing list for information on why this is.

So, after some investigation there's another way to hack this into IPython. We must override the site._Helper class, which is used by IPython to explicitly setup the help system. The following code will do just that when called BEFORE IPython.embed:

import site
site._Helper.__call__ = lambda self, *args, **kwargs: partialhelper(*args, **kwargs)

Are there any other downsides I'm missing here?

like image 55
durden2.0 Avatar answered Oct 05 '22 01:10

durden2.0