on line help for functions

Steven D. Majewski (sdm7g@elvis.med.virginia.edu)
Wed, 16 Mar 1994 23:06:38 -0500

Discovering the linecache functions inspired a hack at
on-line help ( discussed several times long ago. )

'describe( function )'
will try to retrieve the 'def' line from the file,
and any following lines that begin with '#'

After editing the comment lines in my previous post
of redirect.py, exec-ing this file produces:

describe(<function tofile at 20090928>) ==>
def tofile( file, func, *args ):
# apply func( args ), temporarily redirecting stdout to file.

describe(<function tostring at 200908b8>) ==>
def tostring( func, *args ):
# apply func( *args ) with stdout redirected to return string.

describe(<function tolines at 200909a8>) ==>
def tolines( func, *args ):
# apply func( *args ), returning a list of redirected stdout lines.

describe( describe ) ==>
def describe( func ):
# [ PROTOTYPE help function ]
# return the functions documentation string.
# if func.func_doc attributes exists and is not empty,
# that is returned. Else, the functions 'def' line
# and the immediately following comment lines are
# returned, and as a side effect, func_doc is set.

----

If you have applied the patch to add a read/write
func_doc attribute to function objects, describe will
set that string. ( except, that the patch initialized
that string to the function name, so it has to be
nulled out first. )

If not, it should still work ( it checks hasattr()
first ) but it will recall linecache.getline().

It obviously won't work on functions defined interactively
from stdin. I haven't checked to see that when it fails
( from this, or perhaps not being able to find the file )
it fails gracefully. [ Also, it expects SET_LINENO to be
the first byte code sequence. ( Lazy prototype code! )

This is prototype only: I expect to probably change 'func_doc'
attribute to a more neutral name, ( maybe just "_doc" ? ) so
that getattr( thing, '_doc' ) will work for functions, classes,
etc.
Also, in that previous discussion, we touched on literate programming
and GUI oriented help, and I would like to give some thought to
formatting options. ( I'ld consider piping the string thru nroff,
but that would be too unix specific. )

I'll gladly entertain suggestions on the above.
( Maybe we should reserve the initial chars "#{" for
"#{option}, where option might be several things,
including a URL reference. )

- Steve Majewski (804-982-0831) <sdm7g@Virginia.EDU>
- UVA Department of Molecular Physiology and Biological Physics

#!/usr/local/bin/python
# help function prototypes - Steven D. Majewski <sdm7g2Virginia.EDU>
from linecache import getline
from dis import SET_LINENO
import sys

MAXLINES=128

def describe( func ):
# [ PROTOTYPE help function ]
# return the functions documentation string.
# if func.func_doc attributes exists and is not empty,
# that is returned. Else, the functions 'def' line
# and the immediately following comment lines are
# returned, and as a side effect, func_doc is set.
if hasattr( func, 'func_doc' ) :
doc = getattr( func, 'func_doc' )
if doc : return doc
name = func.func_code.co_name
file = func.func_code.co_filename
code = func.func_code.co_code
if ( ord(code[0]) == SET_LINENO ):
lineno = ord(code[1]) + ( ord(code[2]) << 8 )
else: return None
doc = getline( file, lineno )
for lineno in range( lineno+1, lineno+MAXLINES ):
line = getline( file, lineno )
if line[0] == '#' : doc = doc + line
else: break
if hasattr( func, 'func_doc' ):
setattr( func, 'func_doc', doc )
return doc

def _test():
import redirect
for FUN in ( redirect.tofile, redirect.tostring, redirect.tolines ):
FUN.func_doc = ''
print 'describe(' + `FUN` + ') ==>'
print describe( FUN )

if __name__ == '__main__' :
_test()
describe.func_doc = ''
print 'describe( describe ) ==> '
print describe( describe )