[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:
parent
ac5c05b688
commit
a6fd3c241e
|
@ -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):
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue