# Displaying user-defined functions and their docstrings

Let's say I want to create dynamic documentation of functions I wrote, without using the help() function (I don't want the user to care about arguments accepted by my function, nor get cluttered with other stuff like the copyright info, or other stuff that aren't functions).

Here's an attempt:

import inspect

def foo():
"""Return 'foo' string"""
return "foo"

def user_defined_functions():
"""Return names of user-defined functions"""
for name in globals():
if inspect.isfunction(eval(name)):
docstring = eval(name + ".__doc__")
print("{}:\n   {}".format(name, docstring))


And here's the pretty result:

>>> import test
>>> test.user_defined_functions()
user_defined_functions:
Return names of user-defined functions
foo:
Return 'foo' string


If I had just used the help function I could have gotten extra stuff I don't need (I'm not writing a library, so the user doesn't need all of this):

Help on module test:

NAME
test

FUNCTIONS
foo()
Return 'foo' string

user_defined_functions()
Return names of user-defined functions

FILE
/home/wena/src/utils/test.py

• It's not clear how could you use this function on user defined funtions. The users are going to edit this file? Are you going to import their module? Are you forcing them to import user_defined_function? Would you care to explain better? Mar 9 '12 at 9:50
• I was concerned by the term I chose to use, user-defined-functions, due to a lack of a better one in my head. What I meant by it is functions that aren't built into Python. I am maintaining a CLI application (code.google.com/p/wajig) where there are a bunch of functions in a single file, and each of those represent a subcommand. I need this functionality so that when a user types wajig commands, all functions in that file would get displayed with their docstrings. Mar 9 '12 at 14:48

## 2 Answers

Use for name, value in globals().items(): then you don't need to use eval

Peeking inside the source code you linked I'd say that what you need is some sort of command table: a dictiony with all the commands and their aliases should do.

To suggest that, other then the fact that you'll need it for what you asked, is that it's almost already written. It's in that ugly if/elif into the help function, but it's almost already there :)

At the beginning of the file:

COMMANDS_TABLE = {"autoalts": ["autoalternatives"],
"builddeps": ["builddepend", "builddepends", "builddeps"],
#....


With this table your help function will more or less look like:

def help(args):
# ...
for command in in args[1:]:
for func_name,aliases in COMMANDS_TABLE.items():
if command in aliases:
command = func_name
break
# ...


And your listcommands could be:

def listcommands(args):
for func_name in sorted(COMMANDS_TABLE):
print globals()[func_name].__doc__


But this still looks like a hack, and the reason is that a good command table should map the function themselves not their name:

COMMANDS_TABLE = {autoalts: ["autoalternatives"],
builddeps: ["builddepend", "builddepends", "builddeps"],
#....


Everything will be a lot easier, because once you get the function, you can ask whatever you want:

def listcommands(args):
for func in sorted(COMMANDS_TABLE, key=operator.attrgetter('__name__')):
print func.__doc__


Now, this is a good design! You could later on transform those functions into callables and this code will still work :)

In the source code you linked, there's also a util.help() function, that might need the same kind of changes.

• Thanks for taking the time to go as far as looking at that ugly code, especially because that was beyond the scope of the original question. Mar 9 '12 at 18:04
• I don't understand the last paragraph: what eats function name and which one is the better choice. Mar 9 '12 at 18:05
• @Tshepang: Oh, I deleted that... Let's continue in chat if you want, because is not strictly related to your question and I took just a quick look at your code, so I wasn't sure. Mar 9 '12 at 18:12