[Python-3000] PEP Parade

Ron Adam rrr at ronadam.com
Wed May 2 15:24:58 CEST 2007

Phillip J. Eby wrote:
> At 07:37 PM 5/1/2007 -0700, Guido van Rossum wrote:

>> That's one solution. Another solution would be to use GFs in Pydoc to
>> make it overloadable; I'd say pydoc could use a bit of an overhault at
>> this point.
> True enough; until you mentioned that, I'd forgotten that a week or two ago 
> I got an email from somebody working on the pydoc overhaul who mentioned 
> that he had had to work up an ad-hoc generic function implementation for 
> just that reason.  :)

Ah, That would be me.  :-)

I'm still working on it and hope to have it done before the library 
reorganization.  (Minus Python 3000 enhancements since it needs to work 
with python 2.6)

The resulting (yes add-hoc) solution, is basically what I needed to do to 
get it to work nicely.

Talon showed me an example of his that used a decorator to initialize a 
dispatch table.  Which is much easier to maintain over manually editing it 
as I was doing.

Here is an outline of how it generally works.  Maybe you can see where 
proper generic functions might be useful.

INTROSPECTION or (Making a DocInfo Object.)

The actual introspection consists of mostly using the inspect module or 
looking directly at either the attributes, files, or file system.  The end 
product is a data structure containing tagged strings that can be parsed 
and formatted at a later stage.

* A DocInfo object is a DocInfo-list of strings, and more DocInfo objects 
in fifo order, with a tag attribute and a depth first iterator method. 
Ultimately the contents are strings (or string like objects) that came from 
some input source.

(Note: All or any of this can be used outside of pydoc if it is found to be 
generally useful.)

General use:

     1.  Create a inspection dispatcher.

               select = Dispatcher()   # A dispatcher/dictionary.

     2.  Define introspective functions, and use a decorator to
         add them to the dispatcher.

               foo(tag, name, obj):
                  # The tag is added by the dispatcher.
                  items = get_some_info_about_obj()
                  title = DocInfo('title', name)
                  body = DocInfo('list', items)
                  return DocInfo(tag, title, body)

          Do the above for all unique objectes.
         (Functions can have have more than one tag name.)

     3.  Get input from the help function, interactive help, or web
         server request and create a DocInfo structure.


                # parse if needed. (search, topics, inexes, etc...)

                obj = locate(request)
                tag = describe(obj)   # get a descripton that matches a tag.
                return select(tag, request, obj)

Some keys are very general such as 'list', 'item', 'name', 'text', and some 
are specific to the source the data came from... 'package', 'class', 
'module', 'function', etc...

If all you want is to send the text out the way it came in... you can use 
simple string functions.

   result = DocInfo(request).format(str)

That will produce one long everythingruntogether output.

   def repr_line(s):
      return repr(s) + '\n'

   result = DocInfo(request).present(repr_line)

This will put each tagged string on it's own line with quotes around it. 
Since it's a nested structure the quotes will be nested too.


Create a DocFormatter object to format a DocInfo data structure with.

     1.  Define a pre_processor function. (optional)

         This alters the data structure.  For example you can
         rearrange, remove, and/or replace parts before any formatting
         has occured.

     2.  Define a pre_formatter function.  (optional)

         Pre-format input strings at the bottom (depth first) but not
         intermediate result strings.  Any function that takes a string
         and returns a string is fine here.  ie... cgi.escape()

     3.  Define a formatter to format DocInfo list objects according to
         the tags.  (list objects are joined after sub items are formatted.)

	* A function with an if-elif-else structure here is
         perfectly fine, but a dispatcher is better more complex things. ;-)

         (a)  Create a dispatcher object.

         (b)  Add functions to the dispatcher by using decorators and
              tag names.

         * The tags passed to the functions by the dispatcher contains
         all parent tags prepended to them with '.' seperators.  This
         allows you to format based on where something is in addition
         to what it is.

The dispatcher class *is* the formatter function in this case.  The 
__call__ method is used to keep it interchangeable with regular functions. 
  So a method @dispatcher.add(tag1) is used as the decorator to add 
functions to the dispatcher.

     select = Dispatcher()

     @select.add('function', 'method')
     def format_function(tag, info):
        return formatted_info

Multiple tags allow a single function to perform several roles.

     4.  Create a post_formatter.  (optional)

         An example of this might be to replace place holders with
         dynamic content collected during the formatting process.

     5.  Combine the formatters into a single DocFormatter class.

         Example from the html formatter:

             formatter = DocFormatter(preformat=cgi.escape,

The callable, 'select', is the dispatcher, and 'page' is a post-formatter 
that wraps the contents into the final html page with head, navigation bar 
and the body sections.

The DocInfo format method iterates it's contents and calls the formatter on 
it.  The formatter determines what action needs to be taken by what's given 
it and whether or not it's the first time or last time it is called.

And so to put it all together...

        result = DocInfo(request).format(formatter)

I currently have three formatters.

    - text/console
    - html
    - xml

It should be very easy to write other formatters by using these as starting 


More information about the Python-3000 mailing list