Function help to some special words

For some special words, we can use different way to show the help:

For example, help(‘__name__’) will output the text below(or maybe upgrade and be more helpful):

  1. module._name_ : if the module is run as the main module, the value is ‘_main_‘, or the name is the filename of the module. For example:if __name__ == "__main__": ...can control which code don’t run when the module is imported.

  2. cls._name_: the name of the class. It must be str.

Some special variable that have their special way to be defined can use this way to help the users to use them rightly.

1 Like

To be clear, help() prints help on the class of the object. So the help for any string object is, currently, 436 lines of help on str. (The argument can actually be an expession, which is then evaluated to get the help object.)

help has an interactive mode, entered with help(), in which one can enter an unquoted word (module, keyword, or topic), symbol (such as .) to get specific help, or “modules”, “keywords”,
“symbols”, or “topics” for a list of those available. It would be plausible to request that special (double-underscored, dunder) names be recognized, with “dunders” or “specials” returning a list of those recognized. In particular, help> __name__ could give a purpose-written entry similar to that proposed above.

Is it ever actually helpful though for help("topic") to be interpreted as help(str)? Would there be any harm in special casing string inputs?

I have memories of being unable to find help for keywords (neither help(class) or help("class") work) or concepts like string formatting (help(str.format) tells you almost nothing). Likewise with the strange things you find in REPL completions like Ellipsis [1].

I’d really like to see help("concept/topic/keyword/operator/etc") become more helpful and less literal than unconditionally feeding an abbreviated API reference for str into a pager.


  1. help(Ellipsis) gives you the boilerplate signature of the ellipsis class which is neither defined in the same namespace nor meaningful to instantiate – no hint as to what it’s for ↩︎

1 Like

Really? Try that now, see what you get.

Uhmm, now I am really confused as to how I never found that before. Not only does it work now but I worked on the version of Python I first learnt Python on.

I generally think improving help’s output could be useful, so I’ve been thinking about this a little bit over the past couple of days, and doing a little bit of hacking.

My current implementation is ugly and I haven’t thought a lot about specific wording yet, but I wanted to share some early progress in case anyone is interested to continue the conversation. I’ve implemented a few different things so far, each of which has its own benefits and drawbacks, and I guess what I’m curious about specifically is:

  • Is it worth continuing to think about some/all of this?
    • If so, what specifically people would like to see from an improved help function?
      • Examples of help output that could be improved?
      • Examples of phrases for which help(x) currently offers nothing but could be helpful?
    • If not, I had fun thinking about it anyway :slight_smile:.

I’ll briefly summarize my changes so far, and I also went ahead and put my (very WIP) code up at adqm/cpython:pydoc in case that’s helpful (and here’s a diff against the main repo).

@AA-Turner, I see that you’re listed as codeowner for pydoc.py, so CC’ing in case this conversation is of interest.

The changes I’ve made so far amount to:


  • Adding the first paragraph of the library docs (and a link to the relevant library docs for the version of Python from which help was run) to the start of help('mod') where mod is documented in Doc/library. Example below.

    Example help('os') output
    >>> help('os')
    Help on module os:
    
    This module provides a portable way of using operating system
    dependent functionality.  If you just want to read or write a file see
    "open()", if you want to manipulate paths, see the "os.path" module,
    and if you want to read all the lines in all the files on the command
    line see the "fileinput" module.  For creating temporary files and
    directories see the "tempfile" module, and for high-level file and
    directory handling see the "shutil" module.
    
    Full documentation: https://docs.python.org/3.15/library/os.html
    
    NAME
        os - OS routines for NT or Posix depending on what system we're on.
    
    DESCRIPTION
        This exports:
          - all functions from posix or nt, e.g. unlink, stat, etc.
          - os.path is either posixpath or ntpath
    [...]
    

    In some cases, this output is redundant with what’s already shown under DESCRIPTION, but in other cases it provides additional summary information.

    This also includes deprecated modules documented in Doc/library, so help('cgi') shows the same content as the dpo page about it:

    Example help('cgi') output
    >>> help('cgi')
    Help on cgi:
    
    cgi is no longer part of the Python standard library. It was removed
    in Python 3.13 after being deprecated in Python 3.11.  The removal was
    decided in **PEP 594**.
    
    A fork of the module on PyPI can be used instead: legacy-cgi. This is
    a copy of the cgi module, no longer maintained or supported by the
    core Python team.
    
    The last version of Python that provided the "cgi" module was Python
    3.12.
    
    Full documentation: https://docs.python.org/3.15/library/cgi.html
    

  • Changing the way __main__ and __name__ are documented by linking to different parts of the Python docs.

  • Adding specific output for help(help).

    Example help(help) output
    >>> help(help)
    
    help - Interactive Help
    =======================
    
    The built-in help function provides interactive help.  You can make use
    of it in a few different ways:
    
    * Calling help() starts an interactive help session.
    
    * Calling help(x) will have one of two behaviors depending on the type
      of the argument:
    
        * If x is a string, help(x) prints information about the given
          topic.  For example, help("class") will provide information about
          the "class" keyword, and help("math.sqrt") will provide
          information about the "math.sqrt" function.
    
        * If x is not a string, help(x) prints information about x's type.
          For example, help(20) will provide information about the int type. 
    

  • Adding an easy way to add header text to any entry (see Tools/pydoc/README) for information about where I’m currently sitting (though again, I’m not at all tied to this current implementation).

    So help("__name__") in this version adds a prefix showing some information about how __name__ is commonly used (example below).

    Example help('__name__') output
    >>> help('__name__')
    __name__ is a special attribute of certain objects.
    
    1. module.__name__ represents the name of the associated module.
       For example:
    
           >>> import math
           >>> math.__name__
           'math'
    
       If the module is run as the main module the value is "__main__",
       which provides an idiom for detecting whether a module has been run
       directly, versus imported, by checking if __name__ == "__main__"
    
    2. If obj is a class, function, method, descriptor, or generator, then
       obj.__name__ represents the name that obj was given at definition
       time.  For example:
    
           >>> def foo():
           ...     return 42
           ...
           >>> foo.__name__
           "foo"
           >>> bar = foo
           >>> bar.__name__
           "foo"
    
    "__name__ == '__main__'"
    ************************
    
    When a Python module or package is imported, "__name__" is set to the
    module’s name.  Usually, this is the name of the Python file itself
    [...]
    
    

  • Similarly, adding a way to add help about external packages that people may assume are already installed (e.g., pip, numpy) (example below).

    Example help('pip') output
    >>> help('pip')
    Help on pip:
    
    pip is the package installer for Python. You can use it to install
    packages from the Python Package Index and other indexes.
    
    For information about pip, see:
        https://pip.pypa.io/
    
    You do not currently have pip installed for this version of Python.
    For information about installing pip, see:
        https://pip.pypa.io/en/stable/installation/
    

    The last paragraph is only shown if you don’t currently have pip installed (otherwise, the existing help behavior takes over).

1 Like

Just to be clear: This is either incorrect or confusingly worded. help(some_string_object) AFAICT never outputs help for string objects (Except for when the empty string or 'str' is passed in) or evaluates an expression. It always tries to lookup the term in a predefined database of special words (like keywords and a few other predefinied topics) or the __builtins__ namespace.

1 Like

A couple of other things I forgot to mention above that might also be on my radar if there’s interest:

  • Making help useful in non-interactive contexts (perhaps by returning a string rather than using a pager in certain situations)
  • Either updating or deprecating the web interface for pydoc (which gives me a nice hit of nostalgia when I see it, but which I doubt is widely used).

This is true, but I had the same recollection myself, so perhaps there was a time in the past when this wasn’t the case, and that’s what we’re all remembering? Or perhaps we’re all just misremembering…

1 Like

The original post was about changing the help result for special names, with the example being help('__name__'). When I tried that particular example, it does give the same result as help(str). But this is the only case I found today. So I was right in this case (and perhaps a few others) but wrong otherwise ;-(.

In IDLE, which squeezes large outputs (where [...] indicates the displayed button text), I see:

>>> help(str)
[ Squeezed text (436 lines). ]
>>> help('__name__')
[ Squeezed text (436 lines). ]
>>> help('__dict__')
[ Squeezed text (135 lines). ]
>>> help('dict')
[ Squeezed text (135 lines). ]
>>> help('__abs__')
No Python documentation found for '__abs__'.
Use help() to get the interactive help utility.
Use help(str) for help on the str class.

I would have to look at the code in detail to understand the above.

In any case, the fact that string instances are already special-cased suggests that additional special casing could be added for special names.

[Side note: a GUI wrapper for text-oriented help would make it much easier to use. Another topic.]

3 Likes

Right. The pager is nice for those who already know how they work, but I’ve definitely seen beginners struggle to navigate/exit the pager (or, in the case where output is just sent to stdout, have no good way to navigate a big scrollback). I’m not sure what the right™ solution is, but I do know that this limits help’s usefulness for beginners.


The gist is that if the input to help is a string, we check against several special topics first, and then if we didn’t match one, we try to do some amount of name resolution to find built-in objects, modules, etc. The weirdness here is coming from looking things up in builtins, which (as a module) has its own __dict__, __name__, etc.

So:

  • help('__abs__') shows no help available because there’s no built-in __abs__ object
  • help('__dict__') goes to the dict docs because builtins.__dict__ is a dict
  • help('__name__') goes to the str docs because builtins.__name__ is a string

Adding some of these magical dunder things to the set of topics for which we produce bespoke help would be an easy way around this. I don’t think I would want to touch the default algorithm there too much because it works pretty well in most cases.


Anyway, thanks for taking a look! But don’t worry too much about my specific code above; I’ve been reworking it recently.

In the short-term I’m mostly thinking about producing better documentation for stdlib modules, built-in types, and magical dunder stuff. I think I’ve made progress on those fronts, and I’m planning to put in a couple of PR’s in the near future to hopefully get some more feedback on the specifics.

I could have sworn I also tested that particular example but didn’t get the same result. But what @adqm said makes sense, I probably used a different dunder or made a typo or something.


To reduce confusion, I would suggest to not show help if the object found in builtins isn’t a class or function.

And adding explicit entries for all the magic dunders that language (or stdlib) uses is a good idea IMO.

3 Likes