diff --git a/py/apigen/apigen.py b/py/apigen/apigen.py index c86c27c61..d2953dcd3 100644 --- a/py/apigen/apigen.py +++ b/py/apigen/apigen.py @@ -12,6 +12,8 @@ from py.__.apigen import linker from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict +from layout import LayoutPage + def get_documentable_items_pkgdir(pkgdir): """ get all documentable items from an initpkg pkgdir @@ -31,20 +33,27 @@ def get_documentable_items(pkgdir): return pkgname, pkgdict def build(pkgdir, dsa, capture): + # create a linker (link database) for cross-linking l = linker.Linker() + + # create a project.Project instance to contain the LayoutPage instances proj = project.Project() + # output dir if 'APIGEN_TARGET' in os.environ: targetdir = py.path.local(os.environ['APIGEN_TARGET']) else: targetdir = pkgdir.dirpath().join('apigen') targetdir.ensure(dir=True) + # find out what to build all_names = dsa._get_names(filter=lambda x, y: True) namespace_tree = htmlgen.create_namespace_tree(all_names) + + # and build it apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree, - capture) - spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture) + capture, LayoutPage) + spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture, LayoutPage) capture.err.writeorg('preparing namespace pages\n') ns_data = apb.prepare_namespace_pages() diff --git a/py/apigen/html.py b/py/apigen/html.py index d522278e1..67becb66f 100644 --- a/py/apigen/html.py +++ b/py/apigen/html.py @@ -4,7 +4,8 @@ from py.xml import html # HTML related stuff class H(html): class Content(html.div): - pass # style = html.Style(margin_left='15em') + def __init__(self, *args): + super(H.Content, self).__init__(id='apigen-content', *args) class Description(html.div): pass @@ -88,12 +89,29 @@ class H(html): if href: link = H.a(text, href=href) super(H.SourceSnippet, self).__init__( - link, H.div(class_='code', *sourceels)) + link, H.div(*sourceels)) class SourceDef(html.div): def __init__(self, *sourceels): super(H.SourceDef, self).__init__( - H.div(class_='code', *sourceels)) + H.div(*sourceels)) + + class SourceCode(html.div): + style = html.Style(margin_top='1em', margin_bottom='1em') + def __init__(self): + self.linenotable = lntable = H.table(style='float: left') + self.linenotbody = lntbody = H.tbody() + lntable.append(lntbody) + + self.linetable = ltable = H.table() + self.linetbody = ltbody = H.tbody() + ltable.append(ltbody) + + super(H.SourceCode, self).__init__(lntable, ltable) + + def add_line(self, lineno, els): + self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) + self.linetbody.append(H.tr(H.td(class_='code', *els))) class NonPythonSource(html.pre): pass # style = html.Style(margin_left='15em') diff --git a/py/apigen/htmlgen.py b/py/apigen/htmlgen.py index 74fa25ac4..a8a6c8cc2 100644 --- a/py/apigen/htmlgen.py +++ b/py/apigen/htmlgen.py @@ -115,32 +115,65 @@ def create_namespace_tree(dotted_names): ret[ns].append(itempath) return ret -def wrap_page(project, title, contentel, navel, relbase, basepath): - page = LayoutPage(project, title, nav=navel, encoding='UTF-8', +def wrap_page(project, title, contentel, navel, relbase, basepath, + pageclass): + page = pageclass(project, title, nav=navel, encoding='UTF-8', relpath=relbase) page.set_content(contentel) page.setup_scripts_styles(basepath) return page +def enumerate_and_color(codelines, firstlineno, enc): + snippet = H.SourceCode() + tokenizer = source_color.Tokenizer(source_color.PythonSchema) + for i, line in enumerate(codelines): + try: + snippet.add_line(i + firstlineno + 1, + source_html.prepare_line([line], tokenizer, enc)) + except py.error.ENOENT: + # error reading source code, giving up + snippet = org + break + return snippet + +def get_obj(pkg, dotted_name): + full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) + if dotted_name == '': + return pkg + path = dotted_name.split('.') + ret = pkg + for item in path: + marker = [] + ret = getattr(ret, item, marker) + if ret is marker: + raise NameError('can not access %s in %s' % (item, + full_dotted_name)) + return ret + # the PageBuilder classes take care of producing the docs (using the stuff # above) class AbstractPageBuilder(object): + pageclass = LayoutPage + def write_page(self, title, reltargetpath, project, tag, nav): targetpath = self.base.join(reltargetpath) relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), self.base.strpath + '/') - page = wrap_page(project, title, tag, nav, relbase, self.base) + page = wrap_page(project, title, tag, nav, relbase, self.base, + self.pageclass) content = self.linker.call_withbase(reltargetpath, page.unicode) targetpath.ensure() targetpath.write(content.encode("utf8")) class SourcePageBuilder(AbstractPageBuilder): """ builds the html for a source docs page """ - def __init__(self, base, linker, projroot, capture=None): + def __init__(self, base, linker, projroot, capture=None, + pageclass=LayoutPage): self.base = base self.linker = linker self.projroot = projroot self.capture = capture + self.pageclass = pageclass def build_navigation(self, fspath): nav = H.Navigation(class_='sidebar') @@ -191,7 +224,7 @@ class SourcePageBuilder(AbstractPageBuilder): source = fspath.read() sep = get_linesep(source) colored = enumerate_and_color(source.split(sep), 0, enc) - tag = H.SourceDef(*colored) + tag = H.SourceDef(colored) nav = self.build_navigation(fspath) return tag, nav @@ -260,38 +293,10 @@ class SourcePageBuilder(AbstractPageBuilder): '/') self.write_page(title, reltargetpath, project, tag, nav) -def enumerate_and_color(codelines, firstlineno, enc): - tokenizer = source_color.Tokenizer(source_color.PythonSchema) - colored = [] - for i, line in enumerate(codelines): - try: - colored.append(H.span('%04s: ' % (i + firstlineno + 1))) - colored.append(source_html.prepare_line([line], tokenizer, enc)) - colored.append('\n') - except py.error.ENOENT: - # error reading source code, giving up - colored = org - break - return colored - -def get_obj(pkg, dotted_name): - full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) - if dotted_name == '': - return pkg - path = dotted_name.split('.') - ret = pkg - for item in path: - marker = [] - ret = getattr(ret, item, marker) - if ret is marker: - raise NameError('can not access %s in %s' % (item, - full_dotted_name)) - return ret - class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ def __init__(self, base, linker, dsa, projroot, namespace_tree, - capture=None): + capture=None, pageclass=LayoutPage): self.base = base self.linker = linker self.dsa = dsa @@ -299,6 +304,7 @@ class ApiPageBuilder(AbstractPageBuilder): self.projpath = py.path.local(projroot) self.namespace_tree = namespace_tree self.capture = capture + self.pageclass = pageclass pkgname = self.dsa.get_module_name().split('/')[-1] self.pkg = __import__(pkgname) @@ -327,7 +333,7 @@ class ApiPageBuilder(AbstractPageBuilder): firstlineno = func.func_code.co_firstlineno sep = get_linesep(callable_source) org = callable_source.split(sep) - colored = enumerate_and_color(org, firstlineno, enc) + colored = [enumerate_and_color(org, firstlineno, enc)] text = 'source: %s' % (sourcefile,) if is_in_pkg: href = self.linker.get_lazyhref(sourcefile) @@ -657,11 +663,12 @@ class ApiPageBuilder(AbstractPageBuilder): else: enc = 'latin-1' sourcelink = H.div(linktext) - colored = enumerate_and_color(mangled, frame.firstlineno, enc) + colored = [enumerate_and_color(mangled, + frame.firstlineno, enc)] else: sourcelink = H.div('source unknown (%s)' % (sourcefile,)) colored = mangled[:] tbdiv.append(sourcelink) - tbdiv.append(H.div(class_='code', *colored)) + tbdiv.append(H.div(*colored)) return tbdiv diff --git a/py/apigen/style.css b/py/apigen/style.css index 991767005..71ee2631a 100644 --- a/py/apigen/style.css +++ b/py/apigen/style.css @@ -1,5 +1,10 @@ +#apigen-content { + font-size: 0.8em; +} + div.sidebar { font-family: Verdana, Helvetica, Arial, sans-serif; + font-size: 0.9em; width: 155px; vertical-align: top; margin-top: 0.5em; diff --git a/py/apigen/testing/test_apigen_functional.py b/py/apigen/testing/test_apigen_functional.py index 5945e94b3..6ba4b77cb 100644 --- a/py/apigen/testing/test_apigen_functional.py +++ b/py/apigen/testing/test_apigen_functional.py @@ -26,6 +26,14 @@ def setup_fs_project(name): def get_somevar(self): " get_somevar docstring " return self.somevar + + def get_some_source(self): + ret = py.code.Source('''\\ + def foo(): + return 'bar' + ''') + return ret + """)) temp.ensure('pak/sometestsubclass.py').write(py.code.Source("""\ from sometestclass import SomeTestClass diff --git a/py/apigen/testing/test_htmlgen.py b/py/apigen/testing/test_htmlgen.py index f1953f8d7..df6738f74 100644 --- a/py/apigen/testing/test_htmlgen.py +++ b/py/apigen/testing/test_htmlgen.py @@ -47,13 +47,54 @@ 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' - '
') + print repr(div) + assert div == (u'
' + '' + '' + '' + '' + '' + '
1
2
' + '' + '' + '' + '' + '' + '
' + 'def foo():' + '
' + ' print' + ' "bar"' + '
' + '
') + +def test_enumerate_and_color_multiline(): + colored = htmlgen.enumerate_and_color(['code = """\\', 'foo bar', '"""'], + 0, 'ascii') + div = py.xml.html.div(*colored).unicode(indent=0) + print repr(div) + assert div == (u'
' + '' + '' + '' + '' + '' + '' + '
1
2
3
' + '' + '' + '' + '' + '' + '' + '
' + 'code = """\\' + '
' + 'foo bar' + '
' + '"""' + '
' + '
') def test_show_property(): assert htmlgen.show_property('foo')