3

I'm trying to understand how to get useful results when the help function is used to interrogate objects created in my code. I'm puzzled by different behavior for different classes.

Cls1 = type( 'FirstClass', (str,), {'__doc__':'My new class'})
inst1 = Cls1('Hello World')

Cls2 = type( 'SecondClass', (object,), {'__doc__':'My second new class'})
inst2 = Cls2( )

help(inst1) yields No Python documentation found for 'Hello World', while help(inst2) yields:

Help on SecondClass in module __main__ object:

class SecondClass(builtins.object)
 |  My second new class
 |  
...

I would like to create a class based on str and be able to have useful messages displayed by the help function: is there a simple way of achieving this?

M Juckes
  • 299
  • 1
  • 6

2 Answers2

2

If you want to create a subclass of str and show the hints with help built-in, you can use docstrings. For instance the following subclass

class NewString(str):
    """This is a brand new implementation of string type"""
    def test_method(self):
        """This is a test method in the new implementation"""
        pass

has the following output on help(NewString)

class NewString(builtins.str)
 |  This is a brand new implementation of string type
 |  
 |  Method resolution order:
 |      NewString
 |      builtins.str
 |      builtins.object
 |  
 |  Methods defined here:
 |  
 |  test_method(self)
 |      This is a test method in the new implementation
...

But as for all instances of string the help method will not be useful.

The reason it fails is that when passing a str to help build-in it is treated as the name of a function, and as there obviously is not a function named Hello World it shows an error.

Running the following help('help') will output:

Help on _Helper in module _sitebuiltins object:

help = class _Helper(builtins.object)
 |  Define the builtin 'help'.
 |  
 |  This is a wrapper around pydoc.help that provides a helpful message
 |  when 'help' is typed at the Python interactive prompt.
 |  
 |  Calling help() at the Python prompt starts an interactive help session.
 |  Calling help(thing) prints help for the python object 'thing'.
...

which is the help on help.

Henry Harutyunyan
  • 2,355
  • 1
  • 16
  • 22
  • …And `pydoc` makes [no provision](https://github.com/python/cpython/blob/master/Lib/pydoc.py#L1621) to treat `str` subclasses any differently. – Davis Herring Dec 20 '19 at 23:11
  • @DavisHerring Yes this is kind of counter-intuitive. – Henry Harutyunyan Dec 20 '19 at 23:12
  • 1
    "Use docstrings" isn't useful advice when the code in the question *already* uses docstrings. The important part of this answer is the part about the special treatment of `str` instances. – user2357112 Dec 21 '19 at 01:39
  • Thanks, I'd overlooked the fact that `help` treats strings differently by design. However, it appears that this applies not only to strings, but also to classes based on strings (such as NewString in Henry's answer). This begs the question "How does the help function decide whether to treat an object as a string naming a class rather than as a class?". I'll post that as a new question. – M Juckes Dec 21 '19 at 17:19
0

I'm not sure about the convention here, but after searching through the pydoc code I would like to provide a more detailed answer to my own question (the pydoc help text is not very informative on the details). The

When passed an argument with type matching type(""), help checks to see if the argument is:

  • in the list ['keywords', 'symbols', 'topics', 'modules', 'modules *', 'True', 'False', 'None'];
  • a keyword (e.g. "from");
  • a symbol (e.g. '%', '<');
  • a topic (e.g. 'FUNCTIONS', 'METHODS');

This is done in the pydoc.Helper.help method. If a match is found, some specific help text is returned.

If none of the above conditions hold, the program flow continues and the object is passed via pydoc.render_doc to the pydoc.resolve function. Here, if the object is an instance of str (including instances of sub-classes, as interpreted by the built-in function isinstance, the pydoc.resolve function attempts to locate a module defined by the value of the argument, and raises an ImportError exception if there is none.

Hence, help('METHODS') provides help on python methods, while help(Cls1('METHODS')) returns an error (where Cls1 is as defined in the question).

This explains the behaviour I see. To me, the use of the isinstance test in pydoc.resolve, as opposed to the type("") test used in pydoc.Helper.help, appears to be an unnecessary inconsistency. There could, of course, be many reasons for this that I'm not aware of, so I've raised a new question focussed on this issue here.

M Juckes
  • 299
  • 1
  • 6