diff --git a/py/apigen/api.js b/py/apigen/api.js index 3ed872a06..5e7673eb5 100644 --- a/py/apigen/api.js +++ b/py/apigen/api.js @@ -1,7 +1,22 @@ function showhideel(el) { + /* show or hide the element + + sets the value of el.style.display to 'none' or 'block' depending + on the current value + */ if (el.style.display == 'none') { el.style.display = 'block'; } else { el.style.display = 'none'; }; }; + +function getnextsibling(el) { + /* return next non-text sibling (or null) */ + var node = el.nextSibling; + while (node && node.nodeType != 1) { + node = node.nextSibling; + }; + return node; +}; + diff --git a/py/apigen/apigen.py b/py/apigen/apigen.py index c355ffec0..e2688f93c 100644 --- a/py/apigen/apigen.py +++ b/py/apigen/apigen.py @@ -37,7 +37,7 @@ def build(pkgdir, dsa): ns_data = apb.prepare_namespace_pages(namespace_tree) class_names = dsa.get_class_names() - class_data, method_data = apb.prepare_class_pages(namespace_tree, + class_data = apb.prepare_class_pages(namespace_tree, class_names) function_names = dsa.get_function_names() func_data = apb.prepare_function_pages(namespace_tree, function_names) @@ -45,7 +45,7 @@ def build(pkgdir, dsa): apb.build_namespace_pages(ns_data, proj) apb.build_class_pages(class_data, proj) - apb.build_method_pages(method_data, proj) + #apb.build_method_pages(method_data, proj) apb.build_function_pages(func_data, proj) spb.build_pages(source_data, proj, pkgdir) diff --git a/py/apigen/htmlgen.py b/py/apigen/htmlgen.py index 2e74f84c2..ceabb4e55 100644 --- a/py/apigen/htmlgen.py +++ b/py/apigen/htmlgen.py @@ -14,8 +14,11 @@ raw = py.xml.raw # HTML related stuff class H(html): - class Description(html.div): + class Content(html.div): style = html.Style(margin_left='15em') + + class Description(html.div): + pass class NamespaceDescription(Description): pass @@ -79,6 +82,12 @@ class H(html): class ValueDescItem(html.li): pass + class CallStackDescription(Description): + pass + + class CallStackItem(html.div): + class_ = 'callstackitem' + def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect @@ -321,14 +330,23 @@ class ApiPageBuilder(AbstractPageBuilder): else: csource = H.SourceDef('could not get source file') + 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(localname, argdesc), H.Docstring(docstring or H.em('no docstring available')), H.div(H.a('show/hide info', href='#', - onclick=('showhideel(this.parentNode.lastChild);' + onclick=('showhideel(getnextsibling(this));' 'return false;')), - H.div(valuedesc, csource, style='display: none', + H.div(valuedesc, csource, callstack, style='display: none', class_='funcinfo')), ) @@ -397,7 +415,7 @@ class ApiPageBuilder(AbstractPageBuilder): H.NamespaceDef(namespace_dotted_name), H.Docstring(docstring or H.em('no docstring available')) ) - for dotted_name in item_dotted_names: + for dotted_name in sorted(item_dotted_names): itemname = dotted_name.split('.')[-1] if is_private(itemname): continue @@ -412,26 +430,20 @@ class ApiPageBuilder(AbstractPageBuilder): def prepare_class_pages(self, namespace_tree, classes_dotted_names): passed = [] - methodsdata = [] - for dotted_name in classes_dotted_names: + for dotted_name in sorted(classes_dotted_names): parent_dotted_name, _ = split_of_last_part(dotted_name) try: sibling_dotted_names = namespace_tree[parent_dotted_name] except KeyError: # no siblings (built-in module or sth) sibling_dotted_names = [] - tag = self.build_class_view(dotted_name) + tag = H.Content(self.build_class_view(dotted_name)) nav = self.build_navigation(parent_dotted_name, sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) - method_dotted_names = ['%s.%s' % (dotted_name, method_name) for - method_name in - self.dsa.get_class_methods(dotted_name)] - methodsdata += self.prepare_method_pages(namespace_tree, - method_dotted_names) - return passed, methodsdata + return passed def build_class_pages(self, data, project): """ build the full api pages for a set of classes """ @@ -443,7 +455,7 @@ class ApiPageBuilder(AbstractPageBuilder): # XXX note that even though these pages are still built, there's no nav # pointing to them anymore... passed = [] - for dotted_name in method_dotted_names: + for dotted_name in sorted(method_dotted_names): parent_dotted_name, _ = split_of_last_part(dotted_name) module_dotted_name, _ = split_of_last_part(parent_dotted_name) sibling_dotted_names = namespace_tree[module_dotted_name] @@ -462,11 +474,11 @@ class ApiPageBuilder(AbstractPageBuilder): def prepare_function_pages(self, namespace_tree, method_dotted_names): passed = [] - for dotted_name in method_dotted_names: + for dotted_name in sorted(method_dotted_names): # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = namespace_tree[parent_dotted_name] - tag = self.build_callable_view(dotted_name) + tag = H.Content(self.build_callable_view(dotted_name)) nav = self.build_navigation(parent_dotted_name, sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) @@ -485,9 +497,14 @@ class ApiPageBuilder(AbstractPageBuilder): names = namespace_tree.keys() names.sort() - for dotted_name in names: + function_names = self.dsa.get_function_names() + class_names = self.dsa.get_class_names() + for dotted_name in sorted(names): + if dotted_name in function_names or dotted_name in class_names: + continue subitem_dotted_names = namespace_tree[dotted_name] - tag = self.build_namespace_view(dotted_name, subitem_dotted_names) + tag = H.Content(self.build_namespace_view(dotted_name, + subitem_dotted_names)) nav = self.build_navigation(dotted_name, subitem_dotted_names, dotted_name) if dotted_name == '': @@ -588,3 +605,42 @@ class ApiPageBuilder(AbstractPageBuilder): def is_in_pkg(self, sourcefile): return py.path.local(sourcefile).relto(self.projpath) + def build_callsite(self, functionname, call_site): + tbtag = self.gen_traceback(functionname, call_site) + tag = H.CallStackItem( + H.a("%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') + ) + return tag + + def gen_traceback(self, funcname, call_site): + tbdiv = H.div() + for line in call_site: + lineno = line.lineno - line.firstlineno + source = line.source + sourcefile = line.filename + mangled = [] + for i, sline in enumerate(str(source).split('\n')): + if i == lineno: + l = '-> %s' % (sline,) + else: + l = ' %s' % (sline,) + mangled.append(l) + if sourcefile: + linktext = '%s - line %s' % (sourcefile, line.lineno + 1) + # skip py.code.Source objects and source files outside of the + # package + if (not sourcefile.startswith('None') and + self.is_in_pkg(sourcefile)): + href = self.linker.get_lazyhref(sourcefile) + sourcelink = H.a(linktext, href=href) + else: + sourcelink = H.div(linktext) + else: + sourcelink = H.div('source unknown') + tbdiv.append(sourcelink) + tbdiv.append(H.pre('\n'.join(mangled))) + return tbdiv + diff --git a/py/apigen/testing/test_apigen_example.py b/py/apigen/testing/test_apigen_example.py index 51ed352f7..d22474050 100644 --- a/py/apigen/testing/test_apigen_example.py +++ b/py/apigen/testing/test_apigen_example.py @@ -163,9 +163,9 @@ class TestApiPageBuilder(AbstractBuilderTest): _checkhtmlsnippet(html) def test_build_class_pages(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeClass', - 'main.SomeSubClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeClass', + 'main.SomeSubClass']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeClass.html') assert clsfile.check() @@ -173,10 +173,10 @@ class TestApiPageBuilder(AbstractBuilderTest): _checkhtml(html) def test_build_class_pages_instance(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeClass', - 'main.SomeSubClass', - 'main.SomeInstance']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeInstance.html') assert clsfile.check() @@ -187,9 +187,9 @@ class TestApiPageBuilder(AbstractBuilderTest): ]) def test_build_class_pages_nav_links(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', - 'main.SomeClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeSubClass', + 'main.SomeClass']) # fake some stuff that would be built from other methods self.linker.set_link('', 'api/index.html') self.linker.set_link('main', 'api/main.html') @@ -212,9 +212,9 @@ class TestApiPageBuilder(AbstractBuilderTest): _checkhtml(html) def test_build_class_pages_base_link(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', - 'main.SomeClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeSubClass', + 'main.SomeClass']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeSubClass.html') assert clsfile.check() @@ -227,9 +227,9 @@ class TestApiPageBuilder(AbstractBuilderTest): _checkhtml(html) def test_source_links(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', - 'main.SomeClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeSubClass', + 'main.SomeClass']) sourcedata = self.spb.prepare_pages(self.fs_root) self.apb.build_class_pages(data, self.project) self.spb.build_pages(sourcedata, self.project, self.fs_root) diff --git a/py/apigen/testing/test_apigen_functional.py b/py/apigen/testing/test_apigen_functional.py index 7e2311da5..7658b203f 100644 --- a/py/apigen/testing/test_apigen_functional.py +++ b/py/apigen/testing/test_apigen_functional.py @@ -113,9 +113,9 @@ def test_apigen_functional(): html = sometestclass_source.read() assert '