[Python-3000] PEP Parade (original) (raw)
Ron Adam rrr at ronadam.com
Wed May 2 15:24:58 CEST 2007
- Previous message: [Python-3000] PEP Parade
- Next message: [Python-3000] DB API SQL injection issue
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
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.
@select.add('tag')
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.
get_info(request):
# 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.
ADVANCED FORMATTING
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_infoMultiple 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,
formatter=select,
postformat=page)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
- xmlIt should be very easy to write other formatters by using these as starting points.
Cheers, Ron
- Previous message: [Python-3000] PEP Parade
- Next message: [Python-3000] DB API SQL injection issue
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]