diff --git a/py/apigen/html.py b/py/apigen/html.py index 9098a3bfa..7e37e7baf 100644 --- a/py/apigen/html.py +++ b/py/apigen/html.py @@ -31,10 +31,22 @@ class H(html): pass class FunctionDescription(Description): - pass + 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) + super(H.FunctionDescription, self).__init__(fd, ds, fi) class FunctionDef(html.h2): - pass + def __init__(self, name, argdesc): + super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc)) + + class FunctionInfo(html.div): + def __init__(self, valuedesc, csource, callstack): + super(H.FunctionInfo, self).__init__( + H.Hideable('funcinfo', 'funcinfo', valuedesc, csource, + callstack)) class ParameterDescription(html.div): pass @@ -49,11 +61,26 @@ class H(html): pass class NavigationItem(html.div): - pass + def __init__(self, linker, linkid, name, indent, selected): + href = linker.get_lazyhref(linkid) + super(H.NavigationItem, self).__init__((indent * 2 * u'\xa0'), + H.a(name, href=href)) + if selected: + self.attr.class_ = 'selected' class BaseDescription(html.a): pass + class SourceSnippet(html.div): + def __init__(self, text, href, sourceels=None): + if sourceels is None: + sourceels = [] + link = text + if href: + link = H.a(text, href=href) + super(H.SourceSnippet, self).__init__( + link, H.div(class_='code', *sourceels)) + class SourceDef(html.div): pass @@ -74,8 +101,24 @@ class H(html): pass class CallStackDescription(Description): - pass + def __init__(self, callstackdiv): + super(H.CallStackDescription, self).__init__( + H.Hideable('callsites', 'callsites', csdiv)) class CallStackItem(html.div): - class_ = 'callstackitem' + def __init__(self, filename, lineno, traceback): + super(H.CallStackItem, self).__init__( + H.Hideable("stack trace %s - line %s" % (filename, lineno), + 'callstackitem', traceback)) + + class Hideable(html.div): + def __init__(self, name, class_, *content): + super(H.Hideable, self).__init__( + H.div(H.a('show/hide %s' % (name,), + href='#', + onclick=('showhideel(getnextsibling(this));' + 'return false;')), + H.div(style='display: none', + class_=class_, + *content))) diff --git a/py/apigen/htmlgen.py b/py/apigen/htmlgen.py index d386c8c2a..5090f7c4d 100644 --- a/py/apigen/htmlgen.py +++ b/py/apigen/htmlgen.py @@ -50,14 +50,7 @@ def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect # XXX copy and modify formatargspec to produce html - return H.em(inspect.formatargspec(*inspect.getargspec(func))) - -def build_navitem_html(linker, name, linkid, indent, selected): - href = linker.get_lazyhref(linkid) - navitem = H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) - if selected: - navitem.attr.class_ = 'selected' - return navitem + return inspect.formatargspec(*inspect.getargspec(func)) # some helper functionality def source_dirs_files(fspath): @@ -139,15 +132,15 @@ class SourcePageBuilder(AbstractPageBuilder): text = self.projroot.basename else: text = path[i-1] - nav.append(build_navitem_html(self.linker, text, abspath, - indent, False)) + nav.append(H.NavigationItem(self.linker, abspath, text, + indent, False)) indent += 1 # build siblings or children and self if fspath.check(dir=True): # we're a dir, build ourselves and our children dirpath = fspath - nav.append(build_navitem_html(self.linker, dirpath.basename, - dirpath.strpath, indent, True)) + nav.append(H.NavigationItem(self.linker, dirpath.strpath, + dirpath.basename, indent, True)) indent += 1 elif fspath.strpath == self.projroot.strpath: dirpath = fspath @@ -156,13 +149,13 @@ class SourcePageBuilder(AbstractPageBuilder): dirpath = fspath.dirpath() diritems, fileitems = source_dirs_files(dirpath) for dir in diritems: - nav.append(build_navitem_html(self.linker, dir.basename, - dir.strpath, indent, False)) + nav.append(H.NavigationItem(self.linker, dir.strpath, dir.basename, + indent, False)) for file in fileitems: selected = (fspath.check(file=True) and file.basename == fspath.basename) - nav.append(build_navitem_html(self.linker, file.basename, - file.strpath, indent, selected)) + nav.append(H.NavigationItem(self.linker, file.strpath, + file.basename, indent, selected)) return nav re = py.std.re @@ -232,6 +225,7 @@ class SourcePageBuilder(AbstractPageBuilder): except (KeyboardInterrupt, SystemError): raise except: # XXX strange stuff going wrong at times... need to fix + raise exc, e, tb = py.std.sys.exc_info() print '%s - %s' % (exc, e) print @@ -288,41 +282,26 @@ class ApiPageBuilder(AbstractPageBuilder): callable_source = self.dsa.get_function_source(dotted_name) # i assume they're both either available or unavailable(XXX ?) is_in_pkg = self.is_in_pkg(sourcefile) + href = None + text = 'could not get to source file' + colored = [] if sourcefile and callable_source: enc = source_html.get_module_encoding(sourcefile) tokenizer = source_color.Tokenizer(source_color.PythonSchema) firstlineno = func.func_code.co_firstlineno org = callable_source.split('\n') colored = enumerate_and_color(org, firstlineno, enc) + text = 'source: %s' % (sourcefile,) if is_in_pkg: - slink = H.a('source: %s' % (sourcefile,), - href=self.linker.get_lazyhref(sourcefile)) - else: - slink = H.em('source: %s' % (sourcefile,)) - csource = H.div(H.br(), slink, H.br(), - H.SourceDef(H.div(class_='code', *colored))) - else: - csource = H.SourceDef('could not get source file') + href = self.linker.get_lazyhref(sourcefile) - csdiv = H.div(style='display: none') - for cs, _ in self.dsa.get_function_callpoints(dotted_name): - csdiv.append(self.build_callsite(dotted_name, cs)) - callstack = H.CallStackDescription( - H.a('show/hide call sites', - href='#', - onclick='showhideel(getnextsibling(this)); return false;'), - csdiv, - ) - snippet = H.FunctionDescription( - H.FunctionDef('def %s' % (localname,), argdesc), - H.Docstring(docstring or '*no docstring available*'), - H.div(H.a('show/hide info', - href='#', - onclick=('showhideel(getnextsibling(this));' - 'return false;')), - H.div(valuedesc, csource, callstack, style='display: none', - class_='funcinfo')), - ) + csource = H.SourceSnippet(text, href, colored) + callstack = self.dsa.get_function_callpoints(dotted_name) + csitems = [] + for cs, _ in callstack: + csitems.append(self.build_callsite(dotted_name, cs)) + snippet = H.FunctionDescription(localname, argdesc, docstring, + valuedesc, csource, csitems) return snippet @@ -501,8 +480,8 @@ class ApiPageBuilder(AbstractPageBuilder): # top namespace, index.html module_name = self.dsa.get_module_name().split('/')[-1] - navitems.append(build_navitem_html(self.linker, module_name, '', 0, - True)) + navitems.append(H.NavigationItem(self.linker, '', module_name, 0, + True)) def build_nav_level(dotted_name, depth=1): navitems = [] path = dotted_name.split('.')[:depth] @@ -513,8 +492,8 @@ class ApiPageBuilder(AbstractPageBuilder): sibname = sibpath[-1] if is_private(sibname): continue - navitems.append(build_navitem_html(self.linker, sibname, - dn, depth, selected)) + navitems.append(H.NavigationItem(self.linker, dn, sibname, + depth, selected)) if selected: lastlevel = dn.count('.') == dotted_name.count('.') if not lastlevel: @@ -581,15 +560,10 @@ class ApiPageBuilder(AbstractPageBuilder): return py.path.local(sourcefile).relto(self.projpath) def build_callsite(self, functionname, call_site): + print 'building callsite for', functionname tbtag = self.gen_traceback(functionname, reversed(call_site)) - tag = H.CallStackItem( - H.a("show/hide stack trace %s - line %s" % ( - call_site[0].filename, call_site[0].lineno + 1), - href='#', - onclick="showhideel(getnextsibling(this)); return false;"), - H.div(tbtag, style='display: none', class_='callstackitem'), - ) - return tag + return H.CallStackItem(call_site[0].filename, call_site[0].lineno + 1, + tbtag) _reg_source = py.std.re.compile(r'([^>]*)<(.*)>') def gen_traceback(self, funcname, call_site): diff --git a/py/apigen/testing/test_apigen_example.py b/py/apigen/testing/test_apigen_example.py index 3ed757029..20e8033cc 100644 --- a/py/apigen/testing/test_apigen_example.py +++ b/py/apigen/testing/test_apigen_example.py @@ -364,6 +364,16 @@ class TestSourcePageBuilder(AbstractBuilderTest): '

files

']) _checkhtml(html) + def test_build_source_page(self): + data = self.spb.prepare_pages(self.fs_root) + self.spb.build_pages(data, self.project, self.fs_root) + funcsource = self.base.join('source/pkg/func.py.html') + assert funcsource.check(file=True) + html = funcsource.read() + print html + assert ('def ' + 'func(arg1):') in html + def test_build_navigation_root(self): self.spb.prepare_pages(self.fs_root) nav = self.spb.build_navigation(self.fs_root.join('pkg')) diff --git a/py/apigen/testing/test_htmlgen.py b/py/apigen/testing/test_htmlgen.py index a6d09cc74..ec3893293 100644 --- a/py/apigen/testing/test_htmlgen.py +++ b/py/apigen/testing/test_htmlgen.py @@ -19,15 +19,6 @@ def test_create_namespace_tree(): 'pkg': ['pkg.sub', 'pkg.SomeClass', 'pkg.SomeSubClass']} -def test_build_navitem_html(): - l = Linker() - l.set_link('spam.eggs.foo', 'foo.html') - h = htmlgen.build_navitem_html(l, 'foo', 'spam.eggs.foo', 0, False) - assert unicode(h) == u'
foo
' - h = htmlgen.build_navitem_html(l, 'bar', 'spam.eggs.foo', 1, True) - assert unicode(h) == (u'
\xa0\xa0' - u'bar
') - def test_source_dirs_files(): temp = py.test.ensuretemp('test_source_dirs_files') temp.join('dir').ensure(dir=True) @@ -52,3 +43,15 @@ def test_deindent(): assert htmlgen.deindent('foo\n\n bar\n baz\n') == ( 'foo\n\n bar\nbaz\n') +def test_enumerate_and_color(): + colored = htmlgen.enumerate_and_color(['def foo():', ' print "bar"'], 0, + 'ascii') + div = py.xml.html.div(*colored).unicode(indent=0) + assert div == ('
' + ' 1: ' + 'def foo():\n' + ' 2: ' + ' print' + ' "bar"\n' + '
') +