[svn r38043] Changed the way the lazy linking is handled: instead of building the pages

and replacing the links all in memory, pages are now written to disk with
temporary hrefs, which are replaced afterwards.

--HG--
branch : trunk
This commit is contained in:
guido 2007-02-07 01:24:21 +01:00
parent b91e13d5df
commit 8ab4468b5c
7 changed files with 204 additions and 187 deletions

View File

@ -34,7 +34,7 @@ def get_documentable_items(pkgdir):
def build(pkgdir, dsa, capture): def build(pkgdir, dsa, capture):
# create a linker (link database) for cross-linking # create a linker (link database) for cross-linking
l = linker.Linker() l = linker.TempLinker()
# create a project.Project instance to contain the LayoutPage instances # create a project.Project instance to contain the LayoutPage instances
proj = project.Project() proj = project.Project()
@ -52,27 +52,26 @@ def build(pkgdir, dsa, capture):
# and build it # and build it
apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree, apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree,
capture, LayoutPage) proj, capture, LayoutPage)
spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture, LayoutPage) spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, proj, capture,
LayoutPage)
capture.err.writeorg('preparing namespace pages\n')
ns_data = apb.prepare_namespace_pages()
capture.err.writeorg('preparing class pages\n')
class_names = dsa.get_class_names()
class_data = apb.prepare_class_pages(class_names)
capture.err.writeorg('preparing function pages\n')
function_names = dsa.get_function_names()
func_data = apb.prepare_function_pages(function_names)
capture.err.writeorg('preparing source pages\n')
source_data = spb.prepare_pages(pkgdir)
capture.err.writeorg('building namespace pages\n') capture.err.writeorg('building namespace pages\n')
apb.build_namespace_pages(ns_data, proj) apb.build_namespace_pages()
capture.err.writeorg('building class pages\n') capture.err.writeorg('building class pages\n')
apb.build_class_pages(class_data, proj) class_names = dsa.get_class_names()
apb.build_class_pages(class_names)
capture.err.writeorg('building function pages\n') capture.err.writeorg('building function pages\n')
apb.build_function_pages(func_data, proj) function_names = dsa.get_function_names()
apb.build_function_pages(function_names)
capture.err.writeorg('building source pages\n') capture.err.writeorg('building source pages\n')
spb.build_pages(source_data, proj, pkgdir) spb.build_pages(pkgdir)
capture.err.writeorg('replacing temporary links\n')
l.replace_dirpath(targetdir)
capture.err.writeorg('done building documentation\n') capture.err.writeorg('done building documentation\n')

View File

@ -128,6 +128,8 @@ class H(html):
super(H.SourceCode, self).__init__(lntable, ltable) super(H.SourceCode, self).__init__(lntable, ltable)
def add_line(self, lineno, els): def add_line(self, lineno, els):
if els == []:
els = [u'\xa0']
self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) self.linenotbody.append(H.tr(H.td(lineno, class_='lineno')))
self.linetbody.append(H.tr(H.td(class_='code', *els))) self.linetbody.append(H.tr(H.td(class_='code', *els)))

View File

@ -155,23 +155,26 @@ def get_obj(pkg, dotted_name):
class AbstractPageBuilder(object): class AbstractPageBuilder(object):
pageclass = LayoutPage pageclass = LayoutPage
def write_page(self, title, reltargetpath, project, tag, nav): def write_page(self, title, reltargetpath, tag, nav):
targetpath = self.base.join(reltargetpath) targetpath = self.base.join(reltargetpath)
relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep),
self.base.strpath + '/') self.base.strpath + '/')
page = wrap_page(project, title, tag, nav, relbase, self.base, page = wrap_page(self.project, title, tag, nav, relbase, self.base,
self.pageclass) self.pageclass)
content = self.linker.call_withbase(reltargetpath, page.unicode) # we write the page with _temporary_ hrefs here, need to be replaced
# from the TempLinker later
content = page.unicode()
targetpath.ensure() targetpath.ensure()
targetpath.write(content.encode("utf8")) targetpath.write(content.encode("utf8"))
class SourcePageBuilder(AbstractPageBuilder): class SourcePageBuilder(AbstractPageBuilder):
""" builds the html for a source docs page """ """ builds the html for a source docs page """
def __init__(self, base, linker, projroot, capture=None, def __init__(self, base, linker, projroot, project, capture=None,
pageclass=LayoutPage): pageclass=LayoutPage):
self.base = base self.base = base
self.linker = linker self.linker = linker
self.projroot = projroot self.projroot = projroot
self.project = project
self.capture = capture self.capture = capture
self.pageclass = pageclass self.pageclass = pageclass
@ -240,13 +243,11 @@ class SourcePageBuilder(AbstractPageBuilder):
try: try:
tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8'))
except UnicodeError: except UnicodeError:
# XXX we should fix non-ascii support here!!
tag = H.NonPythonSource('no source available (binary file?)') tag = H.NonPythonSource('no source available (binary file?)')
nav = self.build_navigation(fspath) nav = self.build_navigation(fspath)
return tag, nav return tag, nav
def prepare_pages(self, base): def build_pages(self, base):
passed = []
for fspath in [base] + list(base.visit()): for fspath in [base] + list(base.visit()):
if fspath.ext in ['.pyc', '.pyo']: if fspath.ext in ['.pyc', '.pyo']:
continue continue
@ -264,38 +265,36 @@ class SourcePageBuilder(AbstractPageBuilder):
reloutputpath = reloutputpath.replace(os.path.sep, '/') reloutputpath = reloutputpath.replace(os.path.sep, '/')
outputpath = self.base.join(reloutputpath) outputpath = self.base.join(reloutputpath)
self.linker.set_link(str(fspath), reloutputpath) self.linker.set_link(str(fspath), reloutputpath)
passed.append((fspath, outputpath)) self.build_page(fspath, outputpath, base)
return passed
def build_pages(self, data, project, base): def build_page(self, fspath, outputpath, base):
""" build syntax-colored source views """ """ build syntax-colored source views """
for fspath, outputpath in data: if fspath.check(ext='.py'):
if fspath.check(ext='.py'): try:
try: tag, nav = self.build_python_page(fspath)
tag, nav = self.build_python_page(fspath) except (KeyboardInterrupt, SystemError):
except (KeyboardInterrupt, SystemError): raise
raise except: # XXX strange stuff going wrong at times... need to fix
except: # XXX strange stuff going wrong at times... need to fix raise
raise exc, e, tb = py.std.sys.exc_info()
exc, e, tb = py.std.sys.exc_info() print '%s - %s' % (exc, e)
print '%s - %s' % (exc, e) print
print print ''.join(py.std.traceback.format_tb(tb))
print ''.join(py.std.traceback.format_tb(tb)) print '-' * 79
print '-' * 79 del tb
del tb
tag, nav = self.build_nonpython_page(fspath)
elif fspath.check(dir=True):
tag, nav = self.build_dir_page(fspath)
else:
tag, nav = self.build_nonpython_page(fspath) tag, nav = self.build_nonpython_page(fspath)
title = 'sources for %s' % (fspath.basename,) elif fspath.check(dir=True):
reltargetpath = outputpath.relto(self.base).replace(os.path.sep, tag, nav = self.build_dir_page(fspath)
'/') else:
self.write_page(title, reltargetpath, project, tag, nav) tag, nav = self.build_nonpython_page(fspath)
title = 'sources for %s' % (fspath.basename,)
reltargetpath = outputpath.relto(self.base).replace(os.path.sep,
'/')
self.write_page(title, reltargetpath, tag, nav)
class ApiPageBuilder(AbstractPageBuilder): class ApiPageBuilder(AbstractPageBuilder):
""" builds the html for an api docs page """ """ builds the html for an api docs page """
def __init__(self, base, linker, dsa, projroot, namespace_tree, def __init__(self, base, linker, dsa, projroot, namespace_tree, project,
capture=None, pageclass=LayoutPage): capture=None, pageclass=LayoutPage):
self.base = base self.base = base
self.linker = linker self.linker = linker
@ -303,6 +302,7 @@ class ApiPageBuilder(AbstractPageBuilder):
self.projroot = projroot self.projroot = projroot
self.projpath = py.path.local(projroot) self.projpath = py.path.local(projroot)
self.namespace_tree = namespace_tree self.namespace_tree = namespace_tree
self.project = project
self.capture = capture self.capture = capture
self.pageclass = pageclass self.pageclass = pageclass
@ -440,11 +440,9 @@ class ApiPageBuilder(AbstractPageBuilder):
) )
return snippet return snippet
def prepare_class_pages(self, classes_dotted_names): def build_class_pages(self, classes_dotted_names):
passed = [] passed = []
for dotted_name in sorted(classes_dotted_names): for dotted_name in sorted(classes_dotted_names):
#if self.capture:
# self.capture.err.writeorg('preparing: %s\n' % (dotted_name,))
parent_dotted_name, _ = split_of_last_part(dotted_name) parent_dotted_name, _ = split_of_last_part(dotted_name)
try: try:
sibling_dotted_names = self.namespace_tree[parent_dotted_name] sibling_dotted_names = self.namespace_tree[parent_dotted_name]
@ -455,42 +453,13 @@ class ApiPageBuilder(AbstractPageBuilder):
nav = self.build_navigation(dotted_name, False) nav = self.build_navigation(dotted_name, False)
reltargetpath = "api/%s.html" % (dotted_name,) reltargetpath = "api/%s.html" % (dotted_name,)
self.linker.set_link(dotted_name, reltargetpath) self.linker.set_link(dotted_name, reltargetpath)
passed.append((dotted_name, tag, nav, reltargetpath)) title = 'api documentation for %s' % (dotted_name,)
self.write_page(title, reltargetpath, tag, nav)
return passed return passed
def build_class_pages(self, data, project): def build_function_pages(self, method_dotted_names):
""" build the full api pages for a set of classes """
for dotted_name, tag, nav, reltargetpath in data:
#if self.capture:
# self.capture.err.writeorg('building: %s\n' % (dotted_name,))
title = 'api documentation for %s' % (dotted_name,)
self.write_page(title, reltargetpath, project, tag, nav)
def prepare_method_pages(self, method_dotted_names):
# XXX note that even though these pages are still built, there's no nav
# pointing to them anymore...
passed = [] passed = []
for dotted_name in sorted(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 = self.namespace_tree[module_dotted_name]
tag = self.build_callable_view(dotted_name)
nav = self.build_navigation(dotted_name, False)
reltargetpath = "api/%s.html" % (dotted_name,)
self.linker.set_link(dotted_name, reltargetpath)
passed.append((dotted_name, tag, nav, reltargetpath))
return passed
def build_method_pages(self, data, project):
for dotted_name, tag, nav, reltargetpath in data:
title = 'api documentation for %s' % (dotted_name,)
self.write_page(title, reltargetpath, project, tag, nav)
def prepare_function_pages(self, method_dotted_names):
passed = []
for dotted_name in sorted(method_dotted_names):
#if self.capture:
# self.capture.err.writeorg('preparing: %s\n' % (dotted_name,))
# XXX should we create a build_function_view instead? # XXX should we create a build_function_view instead?
parent_dotted_name, _ = split_of_last_part(dotted_name) parent_dotted_name, _ = split_of_last_part(dotted_name)
sibling_dotted_names = self.namespace_tree[parent_dotted_name] sibling_dotted_names = self.namespace_tree[parent_dotted_name]
@ -498,17 +467,11 @@ class ApiPageBuilder(AbstractPageBuilder):
nav = self.build_navigation(dotted_name, False) nav = self.build_navigation(dotted_name, False)
reltargetpath = "api/%s.html" % (dotted_name,) reltargetpath = "api/%s.html" % (dotted_name,)
self.linker.set_link(dotted_name, reltargetpath) self.linker.set_link(dotted_name, reltargetpath)
passed.append((dotted_name, tag, nav, reltargetpath)) title = 'api documentation for %s' % (dotted_name,)
self.write_page(title, reltargetpath, tag, nav)
return passed return passed
def build_function_pages(self, data, project): def build_namespace_pages(self):
for dotted_name, tag, nav, reltargetpath in data:
#if self.capture:
# self.capture.err.writeorg('building: %s\n' % (dotted_name,))
title = 'api documentation for %s' % (dotted_name,)
self.write_page(title, reltargetpath, project, tag, nav)
def prepare_namespace_pages(self):
passed = [] passed = []
module_name = self.dsa.get_module_name().split('/')[-1] module_name = self.dsa.get_module_name().split('/')[-1]
@ -517,8 +480,6 @@ class ApiPageBuilder(AbstractPageBuilder):
function_names = self.dsa.get_function_names() function_names = self.dsa.get_function_names()
class_names = self.dsa.get_class_names() class_names = self.dsa.get_class_names()
for dotted_name in sorted(names): for dotted_name in sorted(names):
#if self.capture:
# self.capture.err.writeorg('preparing: %s\n' % (dotted_name,))
if dotted_name in function_names or dotted_name in class_names: if dotted_name in function_names or dotted_name in class_names:
continue continue
subitem_dotted_names = self.namespace_tree[dotted_name] subitem_dotted_names = self.namespace_tree[dotted_name]
@ -530,17 +491,11 @@ class ApiPageBuilder(AbstractPageBuilder):
else: else:
reltargetpath = 'api/%s.html' % (dotted_name,) reltargetpath = 'api/%s.html' % (dotted_name,)
self.linker.set_link(dotted_name, reltargetpath) self.linker.set_link(dotted_name, reltargetpath)
passed.append((dotted_name, tag, nav, reltargetpath))
return passed
def build_namespace_pages(self, data, project):
for dotted_name, tag, nav, reltargetpath in data:
#if self.capture:
# self.capture.err.writeorg('building: %s\n' % (dotted_name,))
if dotted_name == '': if dotted_name == '':
dotted_name = self.dsa.get_module_name().split('/')[-1] dotted_name = self.dsa.get_module_name().split('/')[-1]
title = 'index of %s namespace' % (dotted_name,) title = 'index of %s namespace' % (dotted_name,)
self.write_page(title, reltargetpath, project, tag, nav) self.write_page(title, reltargetpath, tag, nav)
return passed
def build_navigation(self, dotted_name, build_children=True): def build_navigation(self, dotted_name, build_children=True):
navitems = [] navitems = []

View File

@ -42,6 +42,57 @@ class Linker(object):
finally: finally:
del self.fromlocation del self.fromlocation
class TempLinker(object):
""" performs a similar role to the Linker, but with a different approach
instead of returning 'lazy' hrefs, this returns a simple URL-style
string
the 'temporary urls' are replaced on the filesystem after building the
files, so that means even though a second pass is still required,
things don't have to be built in-memory (as with the Linker)
"""
fromlocation = None
def __init__(self):
self._linkid2target = {}
def get_lazyhref(self, linkid):
return 'apigen.linker://%s' % (linkid,)
def set_link(self, linkid, target):
assert linkid not in self._linkid2target
self._linkid2target[linkid] = target
def get_target(self, tempurl, fromlocation=None):
linkid = '://'.join(tempurl.split('://')[1:])
linktarget = self._linkid2target[linkid]
if fromlocation is not None:
linktarget = relpath(fromlocation, linktarget)
return linktarget
_reg_tempurl = py.std.re.compile('"(apigen.linker:\/\/[^"\s]*)"')
def replace_dirpath(self, dirpath, stoponerrors=True):
""" replace temporary links in all html files in dirpath and below """
for fpath in dirpath.visit('*.html'):
html = fpath.read()
while 1:
match = self._reg_tempurl.search(html)
if not match:
break
tempurl = match.group(1)
try:
html = html.replace('"' + tempurl + '"',
'"' + self.get_target(tempurl,
fpath.relto(dirpath)) + '"')
except KeyError:
if stoponerrors:
raise
html = html.replace('"' + tempurl + '"',
'"apigen.notfound://%s"' % (tempurl,))
fpath.write(html)
def relpath(p1, p2, sep=os.path.sep, back='..', normalize=True): def relpath(p1, p2, sep=os.path.sep, back='..', normalize=True):
""" create a relative path from p1 to p2 """ create a relative path from p1 to p2

View File

@ -89,3 +89,10 @@ ul li {
margin-bottom: 1em; margin-bottom: 1em;
} }
td.lineno {
line-height: 1.1em;
}
td.code {
line-height: 1.1em;
}

View File

@ -1,6 +1,7 @@
# -*- coding: UTF-8 -*-
import py import py
html = py.xml.html html = py.xml.html
from py.__.apigen.linker import Linker from py.__.apigen.linker import TempLinker
from py.__.apigen.htmlgen import * from py.__.apigen.htmlgen import *
from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor
from py.__.apigen.tracer.tracer import Tracer from py.__.apigen.tracer.tracer import Tracer
@ -96,15 +97,9 @@ class AbstractBuilderTest(object):
cls.project = Project() cls.project = Project()
def setup_method(self, meth): def setup_method(self, meth):
class LinkerForTests(Linker):
def get_target(self, linkid):
try:
return super(LinkerForTests, self).get_target(linkid)
except KeyError:
return 'unknown_link_%s' % (linkid,)
self.base = base = py.test.ensuretemp('%s_%s' % ( self.base = base = py.test.ensuretemp('%s_%s' % (
self.__class__.__name__, meth.im_func.func_name)) self.__class__.__name__, meth.im_func.func_name))
self.linker = linker = LinkerForTests() self.linker = linker = TempLinker()
namespace_tree = create_namespace_tree(['main.sub', namespace_tree = create_namespace_tree(['main.sub',
'main.sub.func', 'main.sub.func',
'main.SomeClass', 'main.SomeClass',
@ -116,9 +111,10 @@ class AbstractBuilderTest(object):
self.namespace_tree = namespace_tree self.namespace_tree = namespace_tree
self.apb = ApiPageBuilder(base, linker, self.dsa, self.apb = ApiPageBuilder(base, linker, self.dsa,
self.fs_root.join(self.pkg_name), self.fs_root.join(self.pkg_name),
namespace_tree) namespace_tree, self.project)
self.spb = SourcePageBuilder(base, linker, self.spb = SourcePageBuilder(base, linker,
self.fs_root.join(self.pkg_name)) self.fs_root.join(self.pkg_name),
self.project)
class TestApiPageBuilder(AbstractBuilderTest): class TestApiPageBuilder(AbstractBuilderTest):
def test_build_callable_view(self): def test_build_callable_view(self):
@ -156,8 +152,7 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtmlsnippet(html) _checkhtmlsnippet(html)
def test_build_function_pages(self): def test_build_function_pages(self):
data = self.apb.prepare_function_pages(['main.sub.func']) self.apb.build_function_pages(['main.sub.func'])
self.apb.build_function_pages(data, self.project)
funcfile = self.base.join('api/main.sub.func.html') funcfile = self.base.join('api/main.sub.func.html')
assert funcfile.check() assert funcfile.check()
html = funcfile.read() html = funcfile.read()
@ -169,19 +164,16 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtmlsnippet(html) _checkhtmlsnippet(html)
def test_build_class_pages(self): def test_build_class_pages(self):
data = self.apb.prepare_class_pages(['main.SomeClass', self.apb.build_class_pages(['main.SomeClass', 'main.SomeSubClass'])
'main.SomeSubClass'])
self.apb.build_class_pages(data, self.project)
clsfile = self.base.join('api/main.SomeClass.html') clsfile = self.base.join('api/main.SomeClass.html')
assert clsfile.check() assert clsfile.check()
html = clsfile.read() html = clsfile.read()
_checkhtml(html) _checkhtml(html)
def test_build_class_pages_instance(self): def test_build_class_pages_instance(self):
data = self.apb.prepare_class_pages(['main.SomeClass', self.apb.build_class_pages(['main.SomeClass',
'main.SomeSubClass', 'main.SomeSubClass',
'main.SomeInstance']) 'main.SomeInstance'])
self.apb.build_class_pages(data, self.project)
clsfile = self.base.join('api/main.SomeInstance.html') clsfile = self.base.join('api/main.SomeInstance.html')
assert clsfile.check() assert clsfile.check()
html = clsfile.read() html = clsfile.read()
@ -191,13 +183,11 @@ class TestApiPageBuilder(AbstractBuilderTest):
]) ])
def test_build_class_pages_nav_links(self): def test_build_class_pages_nav_links(self):
data = self.apb.prepare_class_pages(['main.SomeSubClass', self.apb.build_class_pages(['main.SomeSubClass',
'main.SomeClass']) 'main.SomeClass'])
self.apb.prepare_namespace_pages() self.apb.build_namespace_pages()
# fake some stuff that would be built from other methods # fake some stuff that would be built from other methods
self.linker.set_link('', 'api/index.html') self.linker.replace_dirpath(self.base, False)
self.linker.set_link('main', 'api/main.html')
self.apb.build_class_pages(data, self.project)
clsfile = self.base.join('api/main.SomeClass.html') clsfile = self.base.join('api/main.SomeClass.html')
assert clsfile.check() assert clsfile.check()
html = clsfile.read() html = clsfile.read()
@ -217,9 +207,9 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtml(html) _checkhtml(html)
def test_build_class_pages_base_link(self): def test_build_class_pages_base_link(self):
data = self.apb.prepare_class_pages(['main.SomeSubClass', self.apb.build_class_pages(['main.SomeSubClass',
'main.SomeClass']) 'main.SomeClass'])
self.apb.build_class_pages(data, self.project) self.linker.replace_dirpath(self.base, False)
clsfile = self.base.join('api/main.SomeSubClass.html') clsfile = self.base.join('api/main.SomeSubClass.html')
assert clsfile.check() assert clsfile.check()
html = clsfile.read() html = clsfile.read()
@ -231,18 +221,15 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtml(html) _checkhtml(html)
def test_source_links(self): def test_source_links(self):
data = self.apb.prepare_class_pages(['main.SomeSubClass', self.apb.build_class_pages(['main.SomeSubClass', 'main.SomeClass'])
'main.SomeClass']) self.spb.build_pages(self.fs_root)
sourcedata = self.spb.prepare_pages(self.fs_root) self.linker.replace_dirpath(self.base, False)
self.apb.build_class_pages(data, self.project)
self.spb.build_pages(sourcedata, self.project, self.fs_root)
funchtml = self.base.join('api/main.SomeClass.html').read() funchtml = self.base.join('api/main.SomeClass.html').read()
assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1
_checkhtml(funchtml) _checkhtml(funchtml)
def test_build_namespace_pages(self): def test_build_namespace_pages(self):
data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages()
self.apb.build_namespace_pages(data, self.project)
mainfile = self.base.join('api/main.html') mainfile = self.base.join('api/main.html')
assert mainfile.check() assert mainfile.check()
html = mainfile.read() html = mainfile.read()
@ -261,8 +248,7 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtml(otherhtml) _checkhtml(otherhtml)
def test_build_namespace_pages_index(self): def test_build_namespace_pages_index(self):
data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages()
self.apb.build_namespace_pages(data, self.project)
pkgfile = self.base.join('api/index.html') pkgfile = self.base.join('api/index.html')
assert pkgfile.check() assert pkgfile.check()
html = pkgfile.read() html = pkgfile.read()
@ -270,19 +256,18 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtml(html) _checkhtml(html)
def test_build_namespace_pages_subnamespace(self): def test_build_namespace_pages_subnamespace(self):
data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages()
self.apb.build_namespace_pages(data, self.project)
subfile = self.base.join('api/main.sub.html') subfile = self.base.join('api/main.sub.html')
assert subfile.check() assert subfile.check()
html = subfile.read() html = subfile.read()
_checkhtml(html) _checkhtml(html)
def test_build_function_api_pages_nav(self): def test_build_function_api_pages_nav(self):
data = self.apb.prepare_function_pages(['main.sub.func']) self.linker.set_link('main.sub', 'api/main.sub.html')
self.linker.set_link('', 'api/index.html') self.linker.set_link('', 'api/index.html')
self.linker.set_link('main', 'api/main.html') self.linker.set_link('main', 'api/main.html')
self.linker.set_link('main.sub', 'api/main.sub.html') self.apb.build_function_pages(['main.sub.func'])
self.apb.build_function_pages(data, self.project) self.linker.replace_dirpath(self.base, False)
funcfile = self.base.join('api/main.sub.func.html') funcfile = self.base.join('api/main.sub.func.html')
html = funcfile.read() html = funcfile.read()
print html print html
@ -295,31 +280,32 @@ class TestApiPageBuilder(AbstractBuilderTest):
_checkhtml(html) _checkhtml(html)
def test_build_function_navigation(self): def test_build_function_navigation(self):
self.apb.prepare_namespace_pages() self.apb.build_namespace_pages()
self.apb.prepare_function_pages(['main.sub.func']) self.apb.build_function_pages(['main.sub.func'])
self.apb.prepare_class_pages(['main.SomeClass', self.apb.build_class_pages(['main.SomeClass',
'main.SomeSubClass', 'main.SomeSubClass',
'main.SomeInstance']) 'main.SomeInstance'])
nav = self.apb.build_navigation('main.sub.func', False) self.linker.replace_dirpath(self.base, False)
html = nav.unicode(indent=0) html = self.base.join('api/main.sub.func.html').read()
print html.encode('UTF-8') print html
assert (u'<div class="selected"><a href="api/index.html">pkg</a></div>' # XXX NOTE: do not mess with the string below, the spaces between the
u'<div class="selected">\xa0\xa0<a href="api/main.html">main</a></div>' # <div> and <a> are actually UTF-8 \xa0 characters (non-breaking
u'<div>\xa0\xa0\xa0\xa0<a href="api/main.SomeClass.html">' # spaces)!
u'SomeClass</a></div>' assert """\
u'<div>\xa0\xa0\xa0\xa0<a href="api/main.SomeInstance.html">' <div class="sidebar">
u'SomeInstance</a></div>' <div class="selected"><a href="index.html">pkg</a></div>
u'<div>\xa0\xa0\xa0\xa0<a href="api/main.SomeSubClass.html">' <div class="selected">  <a href="main.html">main</a></div>
u'SomeSubClass</a></div>' <div>    <a href="main.SomeClass.html">SomeClass</a></div>
u'<div class="selected">\xa0\xa0\xa0\xa0' <div>    <a href="main.SomeInstance.html">SomeInstance</a></div>
u'<a href="api/main.sub.html">sub</a></div>' <div>    <a href="main.SomeSubClass.html">SomeSubClass</a></div>
u'<div class="selected">\xa0\xa0\xa0\xa0\xa0\xa0' <div class="selected">    <a href="main.sub.html">sub</a></div>
u'<a href="api/main.sub.func.html">func</a></div>' <div class="selected">      <a href="main.sub.func.html">func</a></div>
) in html <div>  <a href="other.html">other</a></div></div>
""" in html
def test_build_root_namespace_view(self): def test_build_root_namespace_view(self):
data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages()
self.apb.build_namespace_pages(data, self.project) self.linker.replace_dirpath(self.base, False)
rootfile = self.base.join('api/index.html') rootfile = self.base.join('api/index.html')
assert rootfile.check() assert rootfile.check()
html = rootfile.read() html = rootfile.read()
@ -328,14 +314,13 @@ class TestApiPageBuilder(AbstractBuilderTest):
class TestSourcePageBuilder(AbstractBuilderTest): class TestSourcePageBuilder(AbstractBuilderTest):
def test_build_pages(self): def test_build_pages(self):
data = self.spb.prepare_pages(self.fs_root) self.spb.build_pages(self.fs_root)
self.spb.build_pages(data, self.project, self.fs_root)
somesource = self.base.join('source/pkg/func.py.html').read() somesource = self.base.join('source/pkg/func.py.html').read()
_checkhtml(somesource) _checkhtml(somesource)
def test_build_pages_nav(self): def test_build_pages_nav(self):
data = self.spb.prepare_pages(self.fs_root) self.spb.build_pages(self.fs_root)
self.spb.build_pages(data, self.project, self.fs_root) self.linker.replace_dirpath(self.base, False)
funcsource = self.base.join('source/pkg/func.py.html') funcsource = self.base.join('source/pkg/func.py.html')
assert funcsource.check(file=True) assert funcsource.check(file=True)
html = funcsource.read() html = funcsource.read()
@ -348,8 +333,8 @@ class TestSourcePageBuilder(AbstractBuilderTest):
]) ])
def test_build_dir_page(self): def test_build_dir_page(self):
data = self.spb.prepare_pages(self.fs_root) self.spb.build_pages(self.fs_root)
self.spb.build_pages(data, self.project, self.fs_root) self.linker.replace_dirpath(self.base, False)
pkgindex = self.base.join('source/pkg/index.html') pkgindex = self.base.join('source/pkg/index.html')
assert pkgindex.check(file=True) assert pkgindex.check(file=True)
html = pkgindex.read() html = pkgindex.read()
@ -365,8 +350,8 @@ class TestSourcePageBuilder(AbstractBuilderTest):
_checkhtml(html) _checkhtml(html)
def test_build_source_page(self): def test_build_source_page(self):
data = self.spb.prepare_pages(self.fs_root) self.spb.build_pages(self.fs_root)
self.spb.build_pages(data, self.project, self.fs_root) self.linker.replace_dirpath(self.base, False)
funcsource = self.base.join('source/pkg/func.py.html') funcsource = self.base.join('source/pkg/func.py.html')
assert funcsource.check(file=True) assert funcsource.check(file=True)
html = funcsource.read() html = funcsource.read()
@ -374,14 +359,14 @@ class TestSourcePageBuilder(AbstractBuilderTest):
assert ('<span class="alt_keyword">def</span> func(arg1)') in html assert ('<span class="alt_keyword">def</span> func(arg1)') in html
def test_build_navigation_root(self): def test_build_navigation_root(self):
self.spb.prepare_pages(self.fs_root) self.spb.build_pages(self.fs_root)
nav = self.spb.build_navigation(self.fs_root.join('pkg')) self.linker.replace_dirpath(self.base)
html = nav.unicode(indent=0) html = self.base.join('source/pkg/index.html').read()
print html.encode('UTF-8') print html
run_string_sequence_test(html, [ run_string_sequence_test(html, [
'href="source/pkg/index.html">pkg', 'href="index.html">pkg',
'href="source/pkg/func.py.html">func.py', 'href="func.py.html">func.py',
'href="source/pkg/someclass.py.html">someclass.py', 'href="someclass.py.html">someclass.py',
'href="source/pkg/somesubclass.py.html">somesubclass.py', 'href="somesubclass.py.html">somesubclass.py',
]) ])

View File

@ -1,5 +1,5 @@
import py import py
from py.__.apigen.linker import Linker, getrelfspath, relpath from py.__.apigen.linker import Linker, TempLinker, getrelfspath, relpath
class TestLinker(object): class TestLinker(object):
def test_get_target(self): def test_get_target(self):
@ -29,6 +29,24 @@ testspec = [
'c:\\foo\\bar c:\\foo ../foo \\', 'c:\\foo\\bar c:\\foo ../foo \\',
] ]
class TestTempLinker(object):
def test_get_target(self):
linker = TempLinker()
temphref = linker.get_lazyhref('py.path.local')
linker.set_link('py.path.local', 'py/path/local.html')
relpath = linker.get_target(temphref)
assert relpath == 'py/path/local.html'
def test_functional(self):
temp = py.test.ensuretemp('TestTempLinker.test_functional')
l = TempLinker()
bar = temp.ensure('foo/bar.html', file=True)
baz = temp.ensure('foo/baz.html', file=True)
l.set_link(baz.strpath, baz.relto(temp))
bar.write('<a href="%s">baz</a>' % (l.get_lazyhref(baz.strpath),))
l.replace_dirpath(temp)
assert bar.read() == '<a href="baz.html">baz</a>'
def gen_check(frompath, topath, sep, expected): def gen_check(frompath, topath, sep, expected):
result = relpath(frompath, topath, sep=sep) result = relpath(frompath, topath, sep=sep)
assert result == expected assert result == expected