[py-svn] r38439 - in py/trunk/py: apigen apigen/testing doc
guido at codespeak.net
guido at codespeak.net
Sun Feb 11 03:04:40 CET 2007
Author: guido
Date: Sun Feb 11 03:04:36 2007
New Revision: 38439
Modified:
py/trunk/py/apigen/apigen.py
py/trunk/py/apigen/html.py
py/trunk/py/apigen/htmlgen.py
py/trunk/py/apigen/layout.py
py/trunk/py/apigen/style.css
py/trunk/py/apigen/testing/test_apigen_example.py
py/trunk/py/apigen/todo.txt
py/trunk/py/doc/confrest.py
Log:
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).
Modified: py/trunk/py/apigen/apigen.py
==============================================================================
--- py/trunk/py/apigen/apigen.py (original)
+++ py/trunk/py/apigen/apigen.py Sun Feb 11 03:04:36 2007
@@ -30,6 +30,7 @@
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):
Modified: py/trunk/py/apigen/html.py
==============================================================================
--- py/trunk/py/apigen/html.py (original)
+++ py/trunk/py/apigen/html.py Sun Feb 11 03:04:36 2007
@@ -25,7 +25,7 @@
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 @@
'*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 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 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',
Modified: py/trunk/py/apigen/htmlgen.py
==============================================================================
--- py/trunk/py/apigen/htmlgen.py (original)
+++ py/trunk/py/apigen/htmlgen.py Sun Feb 11 03:04:36 2007
@@ -14,6 +14,8 @@
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 @@
# 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 @@
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 @@
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 @@
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 @@
)
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 @@
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 @@
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 @@
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 @@
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 @@
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
+
Modified: py/trunk/py/apigen/layout.py
==============================================================================
--- py/trunk/py/apigen/layout.py (original)
+++ py/trunk/py/apigen/layout.py Sun Feb 11 03:04:36 2007
@@ -20,6 +20,7 @@
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)
Modified: py/trunk/py/apigen/style.css
==============================================================================
--- py/trunk/py/apigen/style.css (original)
+++ py/trunk/py/apigen/style.css Sun Feb 11 03:04:36 2007
@@ -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 @@
vertical-align: top;
margin-top: 0.5em;
position: absolute;
+ position: fixed;
top: 130px;
left: 4px;
bottom: 4px;
@@ -34,6 +40,10 @@
list-style-type: none;
}
+h2 {
+ padding-top: 0.5em;
+}
+
.code a {
color: blue;
font-weight: bold;
@@ -42,6 +52,7 @@
.lineno {
line-height: 1.4em;
+ height: 1.4em;
text-align: right;
color: #555;
width: 3em;
@@ -52,6 +63,7 @@
.code {
line-height: 1.4em;
+ height: 1.4em;
padding-left: 1em;
white-space: pre;
font-family: monospace, Monaco;
Modified: py/trunk/py/apigen/testing/test_apigen_example.py
==============================================================================
--- py/trunk/py/apigen/testing/test_apigen_example.py (original)
+++ py/trunk/py/apigen/testing/test_apigen_example.py Sun Feb 11 03:04:36 2007
@@ -37,6 +37,9 @@
" 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 @@
'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 @@
'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 @@
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
Modified: py/trunk/py/apigen/todo.txt
==============================================================================
--- py/trunk/py/apigen/todo.txt (original)
+++ py/trunk/py/apigen/todo.txt Sun Feb 11 03:04:36 2007
@@ -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
+
Modified: py/trunk/py/doc/confrest.py
==============================================================================
--- py/trunk/py/doc/confrest.py (original)
+++ py/trunk/py/doc/confrest.py Sun Feb 11 03:04:36 2007
@@ -33,6 +33,10 @@
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",
More information about the pytest-commit
mailing list