Hi,
I really like the fact that controllers are objects and URL are
translatable into a python path.
I'm also keen of pydoc and the docstrings stuff.
So I developed a small controller and a command line client to
automatically expose documentation from a controller.
Please give me feedback.
Controller Usage:
-----------------
an selfdocumentor for controllers
present the prototypes and documentation of all exposed members of a
controller
use it this way:
>>> import selfdoc
>>> class Foo(selfdoc.SelfDoc):
... '''
... Foo bar controller
... '''
...
... @turbogears.expose()
... def method(self, foo, bar=None):
... '''
... foo bar method
... '''
... return ''
>>>
or alternatively:
>>> import selfdoc
>>> class Foo(turbogears.controllers.Controller):
... '''
... Foo bar controller
... '''
...
... def __init__(self):
... self.doc = selfdoc.SelfDoc(self)
>>>
then access your doc at
http://yoursite/path/to/foo/ or
http://yoursite/path/to/foo/__selfdoc__
Command line client usage:
--------------------------
Command-line client tool for SelfDoc.
$ tg-pydoc http://localhost:8080/some/path
[...]
$ tg-pydoc http://localhost:8080 -m some.path
[...]
$ tg-pydoc http://localhost:8080/some/path --html
<p>[...]
$ echo "http://localhost:8080" > .selfdoc
$ tg-pydoc -m some.path
[...]
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"TurboGears" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/turbogears
-~----------~----~----~----~------~----~------~--~---
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## SelfDoc client
##
## Copyright (C) 2006 Bader Ladjemi <[EMAIL PROTECTED]>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING. If not, write to the
## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
## USA.
"""
Command-line client tool for SelfDoc.
$ tg-pydoc http://localhost:8080/some/path
[...]
$ tg-pydoc http://localhost:8080 -m some.path
[...]
$ tg-pydoc http://localhost:8080/some/path --html
<p>[...]
$ echo "http://localhost:8080" > .selfdoc
$ tg-pydoc -m some.path
[...]
"""
__author__ = "Bader Ladjemi <[EMAIL PROTECTED]>"
__date__ = "8 July 2006"
__version__ = "0.1"
__copyright__ = "GNU GPLv2"
import pydoc
import urllib
import urllib2
import sys
import os
import logging
from optparse import OptionParser
import simplejson
HTTP_PROXY = os.getenv('http_proxy', None)
SELFDOC_FILE = '.selfdoc'
SELFDOC_USER_FILE = os.path.join(os.getenv('HOME'), SELFDOC_FILE)
if HTTP_PROXY is not None:
proxy_handler = urllib2.ProxyHandler({'http': HTTP_PROXY})
urlopen = urllib2.build_opener(proxy_handler)
urllib2.install_opener(urlopen)
class Doc:
"""
Fetch doc from an url
"""
output_format = 'plain'
methods = {
'doc' : "__selfdoc__",
'doc_member': "__selfdoc__member__",
'dir' : "__dir__",
'controllers' : "__dir__controllers__",
'exposed' : "__dir__exposed__",
}
ignored = ('tg_flash', )
def __init__(self, url):
"""
Fetch doc from an url
"""
self.name = None
if url[-1] != '/':
#name or an incomplete url
parent, name = url_parent(url)
if parent is not None:
doc = Doc(parent)
if name in doc.exposed():
self.name = name
url = parent
else:
url += '/'
else:
url += '/'
self.url = url
self.text = None
self.resource = None
def fetch(self, name=None):
"""
Fetch documentation
"""
headers = {'Accept': "text/%s" % self.output_format}
name = name or self.name
if name is None:
url = '%s%s' % (self.url, self.methods['doc'])
else:
params = urllib.urlencode({'name': name})
url = '%s%s?%s' % (self.url, self.methods['doc_member'], params)
req = urllib2.Request(url=url, headers=headers)
try:
self.resource = urllib2.urlopen(req)
except urllib2.HTTPError, err:
logging.fatal(err)
sys.exit(1)
except urllib2.URLError, err:
logging.fatal(err.reason[1])
sys.exit(2)
self.text = self.resource.read()
def exposed(self):
"""
Tuple of exposed methods
"""
return self.fetch_object('exposed')
def controllers(self):
"""
Tuple of controllers
"""
return self.fetch_object('controllers')
def fetch_object(self, method):
"""
Fetch object by method
"""
try:
res = urllib2.urlopen('%s%s' % (self.url, self.methods[method]))
raw_data = simplejson.loads(res.read()).keys()
exposed = tuple(key for key in raw_data if key not in self.ignored)
except urllib2.URLError:
exposed = ()
return exposed
def __str__(self):
"""
Returns documentation
"""
if self.text is None:
self.fetch()
return self.text
def url_parent(url):
"""
tuple of its parent url and last
"""
items = url.rsplit('/', 1)
parent = items[0]+'/'
last = items[-1]
if parent == 'http:/':
parent = None
last = None
return parent, last
def ns_to_path(url, namespace):
"""
translate a namespace into a path string
"""
if url[-1] != '/':
url += '/'
return url+namespace.replace('.', '/')
def command_line_interface():
"""
Command Line Interface for selfdoc client
"""
usage = "usage: %prog [options] url"
parser = OptionParser(usage, version=__version__)
parser.add_option("-m", "--module",
dest="module",
help="fetch documentation for this module")
parser.add_option("--html", action="store_true", dest="html",
help="fetch html output")
(options, args) = parser.parse_args()
if len(args) != 1:
if os.path.exists(SELFDOC_FILE):
url = file(SELFDOC_FILE).read()
elif os.path.exists(SELFDOC_USER_FILE):
url = file(SELFDOC_USER_FILE).read()
else:
parser.error("Please provide an url\
or write it in .selfdoc or ~/.selfdoc")
else:
url = args[0]
if options.module:
url = ns_to_path(url, options.module)
if options.html:
Doc.output_format = 'html'
doc = Doc(url)
output = str(doc)
if not options.html:
pydoc.pager(output)
else:
sys.stdout.write(output)
if __name__ == '__main__':
command_line_interface()
## Selfdoc
## Copyright (C) 2006 <[EMAIL PROTECTED]>
##
## This library is free software; you can redistribute it and/or
## modify it under the terms of the GNU Lesser General Public
## License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
##
## This library is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public
## License along with this library; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
## 02110-1301 USA
"""
SelfDoc, documentation controller for TurboGears
REST compliant.
"""
import pydoc
import inspect
import turbogears
import cherrypy
__author__ = "Bader Ladjemi <[EMAIL PROTECTED]>"
__date__ = "17 June 2006"
__version__ = "0.1"
__copyright__ = "GNU LGPL"
class _HTMLDoc(pydoc.HTMLDoc):
"""
SelfDoc HTMLDoc
prevents htmldoc from making links
"""
def namelink(self, name, *dicts):
"""Make a link for an identifier, given name-to-URL mappings."""
return name
def modulelink(self, object):
"""Make a link for a module."""
return object.__name__
def modpkglink(self, (name, path, ispackage, shadowed)):
"""Make a link for a module or package to display in an index."""
if shadowed:
return self.grey(name)
return name
def classlink(self, object, modname):
"""Make a link for a class."""
name, module = object.__name__, pydoc.sys.modules.get(object.__module__)
return pydoc.classname(object, modname)
helper = {'html': _HTMLDoc(), 'plain': pydoc.text}
def isexposed(member):
"""
Returns True if a method is exposed else False
"""
return getattr(member, 'exposed', False)
def iscontroller(member):
"""
Returns True if an object is a controller else False
"""
return issubclass(member.__class__, turbogears.controllers.Controller)
class SelfDoc(turbogears.controllers.Controller):
"""
an selfdocumentor for controllers
present the prototypes and documentation of all exposed members of a controller
use it this way:
>>> import selfdoc
>>> class Foo(selfdoc.SelfDoc):
... '''
... Foo bar controller
... '''
...
... @turbogears.expose()
... def method(self, foo, bar=None):
... '''
... foo bar method
... '''
... return ''
>>>
or alternatively:
>>> import selfdoc
>>> class Foo(turbogears.controllers.Controller):
... '''
... Foo bar controller
... '''
...
... def __init__(self):
... self.doc = selfdoc.SelfDoc(self)
>>>
then access your doc at
http://yoursite/path/to/foo/ or http://yoursite/path/to/foo/__selfdoc__
"""
selfdoc_methods = ('__selfdoc__',
'__selfdoc__member__',
'__dir__controllers__',
'__dir__exposed__',
'__dir__'
)
def __init__(self, klass, selfdoc=True):
"""
@param klass class to document
@param selfdoc authorize documentation of the selfdoc class
"""
if not inspect.isclass(klass):
klass = klass.__class__
self.selfdoc_klass = klass
self.selfdoc_methods = self.selfdoc_methods+('__dir__',)
self.selfdoc = selfdoc
@turbogears.expose()
def __selfdoc__(self, output_format='plain', selfdoc=False):
"""
Document automically this class
present the prototypes and documentation of all exposed members of this controller
"""
ignored_members = getattr(self, 'selfdoc_methods', ())
if cherrypy.request.headers.has_key('Accept'):
if 'text/html' in cherrypy.request.headers['Accept'].split(';'):
output_format = 'html'
if selfdoc and getattr(self, 'selfdoc', False):
klass = self.__class__
elif hasattr(self, 'selfdoc_klass'):
klass = self.selfdoc_klass
else:
klass = self.__class__
class Klass:
pass
for member_name in dir(klass):
member = getattr(klass, member_name)
if member_name not in ignored_members:
if isexposed(member) and member_name:
setattr(Klass, member_name, member.__composition__[0])
elif iscontroller(member) and member_name:
setattr(Klass, member_name, member)
Klass.__name__ = klass.__name__
Klass.__doc__ = klass.__doc__
cherrypy.response.headers['Content-type'] = 'text/%s' % output_format
return helper[output_format].docclass(Klass)
@turbogears.expose()
def __selfdoc__member__(self, name, output_format='plain'):
"""
Returns documentation for a specific member
"""
if cherrypy.request.headers.has_key('Accept'):
if 'text/html' in cherrypy.request.headers['Accept'].split(';'):
output_format = 'html'
if hasattr(self, 'selfdoc_klass'):
klass = self.selfdoc_klass
else:
klass = self.__class__
member = getattr(klass, name).__composition__[0]
cherrypy.response.headers['Content-type'] = 'text/%s' % output_format
return helper[output_format].docroutine(member)
@turbogears.expose(format='json')
def __dir__controllers__(self):
"""
Returns a dictionary of controllers indexed by their name,
values are their representation.
"""
ignored_members = self.selfdoc_methods
controllers = inspect.getmembers(self, (lambda member: iscontroller(member) and member.__name__ not in ignored_members))
return dict([(member_name, str(member)) for member_name, method in controllers])
@turbogears.expose(format='json')
def __dir__exposed__(self):
"""
Returns a dictionary of exposed methods indexed by their name,
values are their representation.
"""
ignored_members = self.selfdoc_methods
exposed = inspect.getmembers(self, (lambda method: isexposed(method) and method.__name__ not in ignored_members))
return dict([(method_name, str(method)) for method_name, method in exposed])
@turbogears.expose(format='json')
def __dir__(self):
"""
Returns a dictionary of controllers and exposed methods indexed by their name,
values are their representation.
Same as using both __dir__controllers__() and ___dir__exposed__()
"""
ignored_members = self.selfdoc_methods
is_member = (lambda member: (iscontroller(member) or isexposed(member)) and member.__name__ not in ignored_members)
members = inspect.getmembers(self, is_member)
return dict([(member_name, str(member)) for member_name, member in members])
@turbogears.expose()
def index(self):
return self.__selfdoc__()