[svn r38439] Fixed a list of things suggested by hpk: changed method order in class pages,

changed page titles, added links to the api and source index from the nav bar
(also in py/doc html), changed function views, made it possible to remove an
item from the navigation, changed header 'properties' to 'class attributes and
properties', removed duplicate stack traces (in a somewhat unsatisfying way,
needs revisiting later I think).

--HG--
branch : trunk
This commit is contained in:
guido 2007-02-11 03:04:36 +01:00
parent ac5c05b688
commit a6fd3c241e
8 changed files with 130 additions and 22 deletions

View File

@ -30,6 +30,7 @@ def get_documentable_items(pkgdir):
pkgname, pkgdict = get_documentable_items_pkgdir(pkgdir)
from py.__.execnet.channel import Channel
pkgdict['execnet.Channel'] = Channel
Channel.__apigen_hide_from_nav__ = True
return pkgname, pkgdict
def build(pkgdir, dsa, capture):

View File

@ -25,7 +25,7 @@ class H(html):
class ClassDef(html.div):
def __init__(self, classname, bases, docstring, sourcelink,
properties, methods):
attrs, methods):
header = H.h1('class %s(' % (classname,))
for i, (name, href) in py.builtin.enumerate(bases):
if i > 0:
@ -40,9 +40,9 @@ class H(html):
'*no docstring available*'),
sourcelink,
class_='classdoc'))
if properties:
self.append(H.h2('properties:'))
for name, val in properties:
if attrs:
self.append(H.h2('class attributes and properties:'))
for name, val in attrs:
self.append(H.PropertyDescription(name, val))
if methods:
self.append(H.h2('methods:'))
@ -58,20 +58,32 @@ class H(html):
class FunctionDescription(Description):
def __init__(self, localname, argdesc, docstring, valuedesc, csource,
callstack):
fd = H.FunctionDef(localname, argdesc)
ds = H.Docstring(docstring or '*no docstring available*')
fi = H.FunctionInfo(valuedesc, csource, callstack)
infoid = 'info_%s' % (localname.replace('.', '_dot_'),)
docstringid = 'docstring_%s' % (localname.replace('.', '_dot_'),)
fd = H.FunctionDef(localname, argdesc,
onclick=('showhideel('
'document.getElementById("%s")); '
'showhideel('
'document.getElementById("%s")); '
'this.scrollIntoView()' % (
infoid, docstringid)))
ds = H.Docstring(docstring or '*no docstring available*',
id=docstringid)
fi = H.FunctionInfo(valuedesc, csource, callstack,
id=infoid, style="display: none")
super(H.FunctionDescription, self).__init__(fd, ds, fi)
class FunctionDef(html.h2):
def __init__(self, name, argdesc):
super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc))
style = html.Style(cursor='pointer', color='blue')
def __init__(self, name, argdesc, **kwargs):
super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc),
**kwargs)
class FunctionInfo(html.div):
def __init__(self, valuedesc, csource, callstack):
super(H.FunctionInfo, self).__init__(
H.Hideable('funcinfo', 'funcinfo', valuedesc, H.br(), csource,
callstack))
def __init__(self, valuedesc, csource, callstack, **kwargs):
super(H.FunctionInfo, self).__init__(valuedesc, H.br(), csource,
callstack, class_='funcinfo',
**kwargs)
class PropertyDescription(html.div):
def __init__(self, name, value):
@ -86,8 +98,9 @@ class H(html):
class ParameterDescription(html.div):
pass
class Docstring(html.pre):
pass
class Docstring(html.div):
style = html.Style(white_space='pre', color='#666',
margin_left='1em', margin_bottom='1em')
class Navigation(html.div):
#style = html.Style(min_height='99%', float='left', margin_top='1.2em',

View File

@ -14,6 +14,8 @@ sorted = py.builtin.sorted
html = py.xml.html
raw = py.xml.raw
REDUCE_CALLSITES = True
def is_navigateable(name):
return (not is_private(name) and name != '__doc__')
@ -24,7 +26,7 @@ def show_property(name):
# XXX do we need to skip more manually here?
if (name not in dir(object) and
name not in ['__doc__', '__dict__', '__name__', '__module__',
'__weakref__']):
'__weakref__', '__apigen_hide_from_nav__']):
return True
return False
@ -136,10 +138,15 @@ def enumerate_and_color(codelines, firstlineno, enc):
break
return snippet
_get_obj_cache = {}
def get_obj(dsa, pkg, dotted_name):
full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name)
if dotted_name == '':
return pkg
try:
return _get_obj_cache[dotted_name]
except KeyError:
pass
path = dotted_name.split('.')
ret = pkg
for item in path:
@ -147,10 +154,13 @@ def get_obj(dsa, pkg, dotted_name):
ret = getattr(ret, item, marker)
if ret is marker:
try:
return dsa.get_obj(dotted_name)
ret = dsa.get_obj(dotted_name)
except KeyError:
raise NameError('can not access %s in %s' % (item,
full_dotted_name))
else:
break
_get_obj_cache[dotted_name] = ret
return ret
def get_rel_sourcepath(projpath, filename, default=None):
@ -419,6 +429,10 @@ class ApiPageBuilder(AbstractPageBuilder):
def build_methods(self, dotted_name):
ret = []
methods = self.dsa.get_class_methods(dotted_name)
# move all __*__ methods to the back
methods = ([m for m in methods if not m.startswith('_')] +
[m for m in methods if m.startswith('_')])
# except for __init__, which should be first
if '__init__' in methods:
methods.remove('__init__')
methods.insert(0, '__init__')
@ -437,7 +451,8 @@ class ApiPageBuilder(AbstractPageBuilder):
)
for dotted_name in sorted(item_dotted_names):
itemname = dotted_name.split('.')[-1]
if not is_navigateable(itemname):
if (not is_navigateable(itemname) or
self.is_hidden_from_nav(dotted_name)):
continue
snippet.append(
H.NamespaceItem(
@ -463,7 +478,10 @@ class ApiPageBuilder(AbstractPageBuilder):
nav = self.build_navigation(dotted_name, False)
reltargetpath = "api/%s.html" % (dotted_name,)
self.linker.set_link(dotted_name, reltargetpath)
title = 'api documentation for %s' % (dotted_name,)
title = '%s API documentation' % (dotted_name,)
rev = self.get_revision(dotted_name)
if rev:
title += ' [rev. %s]' % (rev,)
self.write_page(title, reltargetpath, tag, nav)
return passed
@ -479,7 +497,10 @@ class ApiPageBuilder(AbstractPageBuilder):
nav = self.build_navigation(dotted_name, False)
reltargetpath = "api/%s.html" % (dotted_name,)
self.linker.set_link(dotted_name, reltargetpath)
title = 'api documentation for %s' % (dotted_name,)
title = '%s API documentation' % (dotted_name,)
rev = self.get_revision(dotted_name)
if rev:
title += ' [rev. %s]' % (rev,)
self.write_page(title, reltargetpath, tag, nav)
return passed
@ -528,6 +549,8 @@ class ApiPageBuilder(AbstractPageBuilder):
sibname = sibpath[-1]
if not is_navigateable(sibname):
continue
if self.is_hidden_from_nav(dn):
continue
navitems.append(H.NavigationItem(self.linker, dn, sibname,
depth, selected))
if selected:
@ -595,10 +618,18 @@ class ApiPageBuilder(AbstractPageBuilder):
def is_in_pkg(self, sourcefile):
return py.path.local(sourcefile).relto(self.projpath)
_processed_callsites = {}
def build_callsites(self, dotted_name):
callstack = self.dsa.get_function_callpoints(dotted_name)
cslinks = []
for i, (cs, _) in enumerate(callstack):
if REDUCE_CALLSITES:
key = (cs[0].filename, cs[0].lineno)
if key in self._processed_callsites:
# process one call site per line of test code when
# REDUCE_CALLSITES is set to True
continue
self._processed_callsites[key] = 1
link = self.build_callsite(dotted_name, cs, i)
cslinks.append(link)
return cslinks
@ -660,3 +691,22 @@ class ApiPageBuilder(AbstractPageBuilder):
tbtag.append(H.div(*colored))
return tbtag
def is_hidden_from_nav(self, dotted_name):
obj = get_obj(self.dsa, self.pkg, dotted_name)
return getattr(obj, '__apigen_hide_from_nav__', False)
def get_revision(self, dotted_name):
obj = get_obj(self.dsa, self.pkg, dotted_name)
try:
sourcefile = inspect.getsourcefile(obj)
except TypeError:
return None
if sourcefile is None:
return None
if sourcefile[-1] in ['o', 'c']:
sourcefile = sourcefile[:-1]
wc = py.path.svnwc(sourcefile)
if wc.check(versioned=True):
return wc.status().rev
return None

View File

@ -20,6 +20,7 @@ class LayoutPage(confrest.PyPage):
self.nav = kwargs.pop('nav')
self.relpath = kwargs.pop('relpath')
super(LayoutPage, self).__init__(*args, **kwargs)
self.project.logo.attr.id = 'logo'
def set_content(self, contentel):
self.contentspace.append(contentel)

View File

@ -2,6 +2,11 @@
font-size: 0.8em;
}
#logo {
position: relative;
position: fixed;
}
div.sidebar {
font-family: Verdana, Helvetica, Arial, sans-serif;
font-size: 0.9em;
@ -9,6 +14,7 @@ div.sidebar {
vertical-align: top;
margin-top: 0.5em;
position: absolute;
position: fixed;
top: 130px;
left: 4px;
bottom: 4px;
@ -34,6 +40,10 @@ ul li {
list-style-type: none;
}
h2 {
padding-top: 0.5em;
}
.code a {
color: blue;
font-weight: bold;
@ -42,6 +52,7 @@ ul li {
.lineno {
line-height: 1.4em;
height: 1.4em;
text-align: right;
color: #555;
width: 3em;
@ -52,6 +63,7 @@ ul li {
.code {
line-height: 1.4em;
height: 1.4em;
padding-left: 1em;
white-space: pre;
font-family: monospace, Monaco;

View File

@ -37,6 +37,9 @@ def setup_fs_project():
" get_somevar docstring "
return self.somevar
SomeInstance = SomeClass(10)
class SomeHiddenClass(object):
" docstring somehiddenclass "
__apigen_hide_from_nav__ = True # hide it from the navigation
"""))
temp.ensure('pkg/somesubclass.py').write(py.code.Source("""\
from someclass import SomeClass
@ -59,6 +62,7 @@ def setup_fs_project():
'main.SomeInstance': ('./someclass.py', 'SomeInstance'),
'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'),
'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'),
'main.SomeHiddenClass': ('./someclass.py', 'SomeHiddenClass'),
'other': ('./somenamespace.py', '*'),
'_test': ('./somenamespace.py', '*'),
})
@ -105,8 +109,9 @@ class AbstractBuilderTest(object):
'main.SomeClass',
'main.SomeSubClass',
'main.SomeInstance',
'main.SomeHiddenClass',
'other.foo',
'other.bar',
'other.baz',
'_test'])
self.namespace_tree = namespace_tree
self.apb = ApiPageBuilder(base, linker, self.dsa,
@ -284,7 +289,8 @@ class TestApiPageBuilder(AbstractBuilderTest):
self.apb.build_function_pages(['main.sub.func'])
self.apb.build_class_pages(['main.SomeClass',
'main.SomeSubClass',
'main.SomeInstance'])
'main.SomeInstance',
'main.SomeHiddenClass'])
self.linker.replace_dirpath(self.base, False)
html = self.base.join('api/main.sub.func.html').read()
print html

View File

@ -3,6 +3,8 @@
special "__*__" methods should come last except for __init__
which comes first
DONE
* the page header should read:
py.path.local API documentation [rev XYZ]
@ -11,10 +13,15 @@
api documentation for path.local
DONE, title changed and if possible (read: if source file in SVN) rev is
retrieved and added
* have the py/doc/ and apigen page layout have
an api and source link in the menu bar
(e.g.: home doc api source contact getting-started issue)
DONE
* function view:
def __init__(self, rawcode):
@ -28,13 +35,21 @@
be "sticking" out (the show/hide info link IMO disrupts this
and it's not visually clear it belongs to the function above it)
DONE, but please review if you like it like this...
* can it be avoided that py.execnet.Channel shows up as a
primary object but still have it documented/linked from
remote_exec()'s "return value"?
DONE: if you set an attribute __hide_from_nav__ to True on an
object somehow, it is hidden from the navigation
* class attributes are not "properties". can they get their
section?
DONE: renamed title to 'class attributes and properties'
(as discussed)
* stacktraces: a lot are "duplicates" like:
/home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37
@ -45,6 +60,12 @@
i think we should by default strip out these duplicates,
this would also reduce the generated html files, right?
DONE, although I'm not happy with it... I'd rather only display call sites
from calls in the test somehow or something...
* allow for flexibility regarding linking from
py/doc/*.txt documents to apigen with respect
to where apigen/ docs are located.
LATER, as discussed

View File

@ -33,6 +33,10 @@ class Page(object):
self.menubar = html.div(
html.a("home", href="home.html", class_="menu"), " ",
html.a("doc", href="index.html", class_="menu"), " ",
html.a("api", href="../../apigen/api/index.html", class_="menu"),
" ",
html.a("source", href="../../apigen/source/index.html",
class_="menu"), " ",
html.a("contact", href="contact.html", class_="menu"), " ",
html.a("getting-started", href="getting-started.html", class_="menu"), " ",
id="menubar",