4

I'm learning to write user-friendly classes and methods and I'd like for users to know how to use them from a terminal. Python's standard help(MYclass) returns eighteen lines I don't want, and that's half a screen for a small window and those just learning python will lose continuity with the previous lines disappearing from the top.

Is there any way I can override what using help(MYclass) (or help(MYmethod)) shows so that it only displays the (in this case) one-line docstring?

While some IDEs show the docstrings in bubbles, terminals don't cooperate. (Do the triple-quoted (docstring) messages in Python appear while typing in IDEs other than IDLE?):

enter image description here

So instead, I've turned to help for help, but help is overly helpful with all those extra lines of template.

Then I thought of redefining help:

def hhelp(x):
    return x.__doc__
help = hhelp

but I decided that this is evil; like redefining the number 7, and I would like help(some builtin) to still work normally, and the selective hijacking to occur only for MYclasses.

There is always

def doc(x):
    return x.__doc__

if I can't find anything that selectively hijacks help().


class A(object):
    """instantiate a = A(x), then use a.sqr() to get x**2"""
    def __init__(self, x):
            self.x = x
    def sqr(self):
            return x**2

results in nineteen lines. I'd only like my one-line docstring to show.

Help on class A in module __main__:

class A(__builtin__.object)
 |  instantiate a = A(x), then use a.sqr() to get x**2
 |  
 |  Methods defined here:
 |  
 |  __init__(self, x)
 |  
 |  sqr(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
Tom Zych
  • 13,329
  • 9
  • 36
  • 53
uhoh
  • 3,713
  • 6
  • 42
  • 95

2 Answers2

1

As help(help) indicates it relies on pydoc, specifically pydoc.help.

| Define the builtin 'help'.
| This is a wrapper around pydoc.help (with a twist).

Inspecting the source code we find that it relies on TextDoc.docclass to generate the help text for classes. So we can monkeypatch that method in order to generate our own help text:1

import pydoc

def docclass(self, object, *args, **kwargs):
    return pydoc.getdoc(object)

pydoc.TextDoc.docclass = docclass

Now we'll only get the __doc__ string in the help text:

class Foo:
    """This is the docstring."""

    def foo(self):
        return None

help(Foo)

# Output:
# Help on class Foo in module __main__:
# 
# This is the docstring.

1. The syntax is specific to Python 2.7 since the OP uses the corresponding tag. Though it seems to work with other versions of Python as well.

a_guest
  • 34,165
  • 12
  • 64
  • 118
1

You can monkeypatch the built-in help function to show the __doc__ string for classes and to fall back on the original help for other objects:

import inspect
import pydoc

class Helper(object):
    def __init__(self):
        self.help = __builtins__.help

    def __call__(self, obj):
        if inspect.isclass(obj):
            return pydoc.pager(obj.__doc__)
        else:
            return self.help(obj)

__builtins__.help = Helper()

Which produces the following output:

class Foo:
    """This is the docstring."""

    def foo(self):
        return None

help(Foo)

# Output:
# This is the docstring.
a_guest
  • 34,165
  • 12
  • 64
  • 118
  • This is *exactly* what I need, thank you! If I take some time to stop and smell the bananas I could probably come up with some way to "refine the monkey" to handle my classes but leave things like `list` alone. – uhoh Jan 02 '19 at 02:48
  • 1
    @uhoh You could use `inspect.isclass(obj) and not any(map(lambda x: x is obj, (list, tuple, set, dict, ...)))` or if you have all your classes inherit from some custom base class `MyBase` you could use `issubclass(obj, MyBase)`. To be more complete on the above enumeration you could also use `map(lambda x: getattr(__builtins__, x), dir(__builtins__)` to obtain a full list of built-in objects. – a_guest Jan 02 '19 at 10:41
  • I was in the middle of formulating a new question on just that, I'll plug those in to my unfinished abstracted script to see if you have pre-answered it. I'll flag you here if I decide to go ahead with the post. Thank you again! – uhoh Jan 02 '19 at 11:24