diff --git a/py/apigen/apigen.py b/py/apigen/apigen.py index 6a527d8c3..69777b8e0 100644 --- a/py/apigen/apigen.py +++ b/py/apigen/apigen.py @@ -1,7 +1,4 @@ """ run 'py.test --apigen=' to get documentation exported - - exports to /tmp/output by default, set the environment variable - 'APIGEN_TARGET' to override """ import os @@ -11,6 +8,7 @@ from py.__.apigen import htmlgen from py.__.apigen import linker from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict +from py.__.doc.conftest import get_apigenpath from layout import LayoutPage @@ -41,10 +39,8 @@ def build(pkgdir, dsa, capture): 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') + from py.__.conftest import option + targetdir = get_apigenpath() targetdir.ensure(dir=True) # find out what to build diff --git a/py/apigen/htmlgen.py b/py/apigen/htmlgen.py index 459ab4a9b..5d705f385 100644 --- a/py/apigen/htmlgen.py +++ b/py/apigen/htmlgen.py @@ -119,10 +119,9 @@ def create_namespace_tree(dotted_names): ret[ns].append(itempath) return ret -def wrap_page(project, title, contentel, navel, relbase, basepath, +def wrap_page(project, title, targetpath, contentel, navel, basepath, pageclass): - page = pageclass(project, title, nav=navel, encoding='UTF-8', - relpath=relbase) + page = pageclass(project, title, targetpath, nav=navel, encoding='UTF-8') page.set_content(contentel) page.setup_scripts_styles(basepath) return page @@ -180,7 +179,7 @@ class AbstractPageBuilder(object): targetpath = self.base.join(reltargetpath) relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), self.base.strpath + '/') - page = wrap_page(self.project, title, tag, nav, relbase, self.base, + page = wrap_page(self.project, title, targetpath, tag, nav, self.base, self.pageclass) # we write the page with _temporary_ hrefs here, need to be replaced # from the TempLinker later diff --git a/py/apigen/layout.py b/py/apigen/layout.py index 1b54d4d79..81cd953d3 100644 --- a/py/apigen/layout.py +++ b/py/apigen/layout.py @@ -6,6 +6,7 @@ import py from py.__.doc import confrest from py.__.apigen import linker +from py.__.doc.conftest import get_apigenpath, get_docpath here = py.magic.autopath().dirpath() @@ -18,10 +19,14 @@ class LayoutPage(confrest.PyPage): def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') - self.relpath = kwargs.pop('relpath') super(LayoutPage, self).__init__(*args, **kwargs) + self.relpath = self.get_relpath() self.project.logo.attr.id = 'logo' + def get_relpath(self): + return linker.relpath(self.targetpath.strpath, + get_apigenpath().strpath) + '/' + def set_content(self, contentel): self.contentspace.append(contentel) @@ -29,23 +34,6 @@ class LayoutPage(confrest.PyPage): super(LayoutPage, self).fill() self.body.insert(0, self.nav) - def _getdocrelpath(self, default="../py/doc"): - docrel = py.std.os.environ.get("APIGEN_DOCRELPATH", default) - return docrel.rstrip("/") + "/" - - def a_docref(self, name, relhtmlpath): - docrelpath = self._getdocrelpath() - relnew = self.relpath + docrelpath + relhtmlpath - return super(LayoutPage, self).a_docref(name, relnew) - - def a_apigenref(self, name, relhtmlpath): - # XXX the path construction is probably rather too complicated - # but i reused the same logic that was there - # before. - docrelpath = self._getdocrelpath() - relnew = self.relpath + docrelpath + relhtmlpath - return super(LayoutPage, self).a_apigenref(name, relnew) - def setup_scripts_styles(self, copyto=None): for path, name in self.stylesheets: if copyto: diff --git a/py/apigen/testing/test_apigen_example.py b/py/apigen/testing/test_apigen_example.py index 8d67ee558..3406dc3ce 100644 --- a/py/apigen/testing/test_apigen_example.py +++ b/py/apigen/testing/test_apigen_example.py @@ -5,6 +5,7 @@ from py.__.apigen.linker import TempLinker from py.__.apigen.htmlgen import * from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor from py.__.apigen.tracer.tracer import Tracer +from py.__.apigen.layout import LayoutPage from py.__.apigen.project import Project from py.__.test.web import webcheck from py.__.apigen.conftest import option @@ -94,6 +95,10 @@ def _checkhtmlsnippet(htmlstring): #"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n""" + unicode(h) #_checkhtml(newstring) +class LayoutTestPage(LayoutPage): + def get_relpath(self): + return '../' + class AbstractBuilderTest(object): def setup_class(cls): temp = py.test.ensuretemp('apigen_example') @@ -121,6 +126,7 @@ class AbstractBuilderTest(object): self.spb = SourcePageBuilder(base, linker, self.fs_root.join(self.pkg_name), self.project) + self.apb.pageclass = self.spb.pageclass = LayoutTestPage class TestApiPageBuilder(AbstractBuilderTest): def test_build_callable_view(self): @@ -396,7 +402,7 @@ class TestSourcePageBuilder(AbstractBuilderTest): html = funcsource.read() print html run_string_sequence_test(html, [ - 'href="../../style.css"', + 'href="../style.css"', 'pkg', 'someclass.py', 'somesubclass.py', @@ -410,7 +416,7 @@ class TestSourcePageBuilder(AbstractBuilderTest): html = pkgindex.read() print html run_string_sequence_test(html, [ - 'href="../../style.css"', + 'href="../style.css"', 'pkg', 'func.py', 'someclass.py', diff --git a/py/apigen/testing/test_apigen_functional.py b/py/apigen/testing/test_apigen_functional.py index 1f3f01679..aabef01c6 100644 --- a/py/apigen/testing/test_apigen_functional.py +++ b/py/apigen/testing/test_apigen_functional.py @@ -125,10 +125,10 @@ def test_apigen_functional(): pydir = py.magic.autopath().dirpath().dirpath().dirpath() pakdir = fs_root.join('pak') if py.std.sys.platform == 'win32': - cmd = ('set APIGEN_TARGET=%s && set PYTHONPATH=%s && ' + cmd = ('set APIGENPATH=%s && set PYTHONPATH=%s && ' 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) else: - cmd = ('APIGEN_TARGET="%s" PYTHONPATH="%s" ' + cmd = ('APIGENPATH="%s" PYTHONPATH="%s" ' 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) try: output = py.process.cmdexec('%s --apigen="%s/apigen.py" "%s"' % ( diff --git a/py/bin/_docgen.py b/py/bin/_docgen.py index df8bbf4a7..cbbd1453c 100755 --- a/py/bin/_docgen.py +++ b/py/bin/_docgen.py @@ -23,7 +23,7 @@ def build_apigen_docs(targetpath, testargs=''): run_tests(pypath, 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( targetpath,), - testargs + ' --apigen="%s/apigen/apigen.py"' % (pypath,)) + '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) def build_docs(targetpath, testargs): docpath = pypath.join('doc') diff --git a/py/bin/_update_website.py b/py/bin/_update_website.py index 521334084..4d4b42784 100755 --- a/py/bin/_update_website.py +++ b/py/bin/_update_website.py @@ -22,32 +22,32 @@ def rsync(pkgpath, apidocspath, gateway, remotepath): rs.add_target(gateway, remotepath, delete=True) rs.send() -def run_tests(pkgpath, args='', captureouterr=False): +def run_tests(pkgpath, apigenpath, args='', captureouterr=False): """ run the unit tests and build the docs """ pypath = py.__package__.getpath() pytestpath = pypath.join('bin/py.test') # XXX this would need a Windows specific version if we want to allow # running this script on that platform, but currently --apigen doesn't # work there anyway... - apigenpath = pkgpath.join('apigen/apigen.py') # XXX be more general here? - if not apigenpath.check(file=True): - apigenpath = pypath.join('apigen/apigen.py') - cmd = 'PYTHONPATH="%s:%s" python "%s" %s --apigen="%s" "%s"' % ( - pypath.dirpath(), - pkgpath.dirpath(), - pytestpath, - args, - apigenpath, - pkgpath, - ) + apigenscript = pkgpath.join('apigen/apigen.py') # XXX be more general here? + if not apigenscript.check(file=True): + apigenscript = pypath.join('apigen/apigen.py') + cmd = ('APIGENPATH="%s" PYTHONPATH="%s:%s" python ' + '"%s" %s --apigen="%s" "%s"' % (apigenpath, pypath.dirpath(), + pkgpath.dirpath(), pytestpath, + args, apigenscript, + pkgpath)) if captureouterr: cmd += ' > /dev/null 2>&1' - status = py.std.os.system(cmd) - return status + try: + output = py.process.cmdexec(cmd) + except py.error.Error, e: + return e.err or str(e) + return None def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): print 'running tests' - errors = run_tests(pkgpath, args) + errors = run_tests(pkgpath, apidocspath, args) if errors: print >>sys.stderr, \ 'Errors while running the unit tests: %s' % (errors,) diff --git a/py/conftest.py b/py/conftest.py index 229b0793e..0b96badb1 100644 --- a/py/conftest.py +++ b/py/conftest.py @@ -19,11 +19,22 @@ nomagic = False import py Option = py.test.config.Option +here = py.magic.autopath().dirpath() + option = py.test.config.addoptions("execnet options", Option('-S', '', action="store", dest="sshtarget", default=None, help=("target to run tests requiring ssh, e.g. " "user@codespeak.net")), + Option('', '--apigenpath', + action="store", dest="apigenpath", + default=here.join("../apigen").strpath, + type="string", + help="absolute path to where apigen docs are built"), + Option('', '--docpath', + action='store', dest='docpath', + default=here.join('doc').strpath, type='string', + help='absolute path to where the docs are built'), ) dist_rsync_roots = ['.'] diff --git a/py/doc/confrest.py b/py/doc/confrest.py index 3137da2d4..900a674db 100644 --- a/py/doc/confrest.py +++ b/py/doc/confrest.py @@ -1,7 +1,8 @@ import py from py.__.misc.rest import convert_rest_html, strip_html_header from py.__.misc.difftime import worded_time -from py.__.doc.conftest import get_apigen_relpath +from py.__.doc.conftest import get_apigenpath, get_docpath +from py.__.apigen.linker import relpath mydir = py.magic.autopath().dirpath() html = py.xml.html @@ -10,9 +11,11 @@ class Page(object): doctype = ('\n') - def __init__(self, project, title, stylesheeturl=None, type="text/html", encoding="ISO-8859-1"): + def __init__(self, project, title, targetpath, stylesheeturl=None, + type="text/html", encoding="ISO-8859-1"): self.project = project self.title = project.prefix_title + title + self.targetpath = targetpath self.stylesheeturl = stylesheeturl self.type = type self.encoding = encoding @@ -23,22 +26,26 @@ class Page(object): self.fill() def a_docref(self, name, relhtmlpath): - return html.a(name, class_="menu", href=relhtmlpath) + docpath = get_docpath() + return html.a(name, class_="menu", + href=relpath(self.targetpath.strpath, + docpath.join(relhtmlpath).strpath)) def a_apigenref(self, name, relhtmlpath): - apigen_relpath = get_apigen_relpath() - path = apigen_relpath.rstrip("/") + "/" + relhtmlpath - return html.a(name, href=path, class_="menu") + apipath = get_apigenpath() + return html.a(name, class_="menu", + href=relpath(self.targetpath.strpath, + apipath.join(relhtmlpath).strpath)) def fill_menubar(self): items = [ - self.a_docref("index", "index.html"), - self.a_apigenref("api", "api/index.html"), - self.a_apigenref("source", "source/index.html"), - self.a_docref("contact", "contact.html"), - self.a_docref("download", "download.html"), - html.a("contact", href="contact.html", class_="menu"), - html.a("download", href="download.html", class_="menu"), + self.a_docref("index", "index.html"), + self.a_apigenref("api", "api/index.html"), + self.a_apigenref("source", "source/index.html"), + self.a_docref("contact", "contact.html"), + self.a_docref("download", "download.html"), + html.a("contact", href="contact.html", class_="menu"), + html.a("download", href="download.html", class_="menu"), ] items2 = [items.pop(0)] sep = " " @@ -47,26 +54,26 @@ class Page(object): items2.append(item) self.menubar = html.div(id="menubar", *items2) - def fill(self): - content_type = "%s;charset=%s" %(self.type, self.encoding) - self.head.append(html.title(self.title)) + def fill(self): + content_type = "%s;charset=%s" %(self.type, self.encoding) + self.head.append(html.title(self.title)) self.head.append(html.meta(name="Content-Type", content=content_type)) - if self.stylesheeturl: + if self.stylesheeturl: self.head.append( - html.link(href=self.stylesheeturl, - media="screen", rel="stylesheet", + html.link(href=self.stylesheeturl, + media="screen", rel="stylesheet", type="text/css")) self.fill_menubar() self.metaspace = html.div( - html.div(self.title, class_="project_title"), + html.div(self.title, class_="project_title"), self.menubar, id='metaspace') - self.body.append(self.project.logo) - self.body.append(self.metaspace) + self.body.append(self.project.logo) + self.body.append(self.metaspace) self.contentspace = html.div(id="contentspace") - self.body.append(self.contentspace) + self.body.append(self.contentspace) def unicode(self, doctype=True): page = self._root.unicode() @@ -76,12 +83,14 @@ class Page(object): return page class PyPage(Page): - def fill(self): - super(PyPage, self).fill() + def get_menubar(self): + menubar = super(PyPage, self).get_menubar() # base layout - self.menubar.append( - html.a("issue", href="https://codespeak.net/issue/py-dev/", class_="menu"), - ) + menubar.append( + html.a("issue", href="https://codespeak.net/issue/py-dev/", + class_="menu"), + ) + return menubar def getrealname(username): @@ -99,8 +108,9 @@ def getrealname(username): return username -class Project: - stylesheet = 'style.css' +class Project: + # string for url, path for local file + stylesheet = mydir.join('style.css') title = "py lib" prefix_title = "" # we have a logo already containing "py lib" encoding = 'latin1' @@ -118,15 +128,23 @@ class Project: def process(self, txtpath): encoding = self.encoding content = self.get_content(txtpath, encoding) - stylesheet = self.stylesheet - if not stylesheet.startswith('http') and \ - not txtpath.dirpath(stylesheet).check(): - stylesheet = None + docpath = get_docpath() + reloutputpath = txtpath.new(ext='.html').relto(mydir) + outputpath = docpath.join(reloutputpath) - content = convert_rest_html(content, txtpath, stylesheet=stylesheet, encoding=encoding) + stylesheet = self.stylesheet + if isinstance(self.stylesheet, py.path.local): + if not docpath.join(stylesheet.basename).check(): + stylesheet.copy(docpath) + stylesheet = relpath(outputpath.strpath, + docpath.join(stylesheet.basename).strpath) + + content = convert_rest_html(content, txtpath, + stylesheet=stylesheet, encoding=encoding) content = strip_html_header(content, encoding=encoding) - page = self.Page(self, "[%s] " % txtpath.purebasename, stylesheeturl=stylesheet) + page = self.Page(self, "[%s] " % txtpath.purebasename, + outputpath, stylesheeturl=stylesheet) try: svninfo = txtpath.info() @@ -142,6 +160,5 @@ class Project: id = 'docinfoline')) page.contentspace.append(py.xml.raw(content)) - htmlpath = txtpath.new(ext='.html') - htmlpath.write(page.unicode().encode(encoding)) + outputpath.ensure().write(page.unicode().encode(encoding)) diff --git a/py/doc/conftest.py b/py/doc/conftest.py index bbdb7e1ba..111d11e04 100644 --- a/py/doc/conftest.py +++ b/py/doc/conftest.py @@ -1,6 +1,10 @@ from __future__ import generators import py from py.__.misc import rest +from py.__.apigen.linker import relpath +import os + +mypath = py.magic.autopath().dirpath() Option = py.test.config.Option option = py.test.config.addoptions("documentation check options", @@ -12,16 +16,24 @@ option = py.test.config.addoptions("documentation check options", action="store_true", dest="forcegen", default=False, help="force generation of html files even if they appear up-to-date" ), - Option('', '--apigenrelpath', - action="store", dest="apigen_relpath", default="../../apigen", - type="string", - help=("specify the relative path to apigen (used for link " - "generation)") - ) ) +def get_apigenpath(): + from py.__.conftest import option + path = os.environ.get('APIGENPATH') + if path is None: + path = option.apigenpath + return py.path.local(path) + +def get_docpath(): + from py.__.conftest import option + path = os.environ.get('DOCPATH') + if path is None: + path = option.docpath + return py.path.local(path) + def get_apigen_relpath(): - return py.test.config.option.apigen_relpath.rstrip('\/') + "/" + return relpath(get_apigenpath().strpath, get_docpath().strpath) def deindent(s, sep='\n'): leastspaces = -1 @@ -82,7 +94,7 @@ def restcheck(path): def _checkskip(lpath): if not option.forcegen: if lpath.ext == '.txt': - htmlpath = lpath.new(ext='.html') + htmlpath = get_docpath().join(lpath.new(ext='.html').relto(mypath)) if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): py.test.skip("html file is up to date, use --forcegen to regenerate") #return [] # no need to rebuild diff --git a/py/misc/testing/test_update_website.py b/py/misc/testing/test_update_website.py index 976aac1d7..845747ebf 100644 --- a/py/misc/testing/test_update_website.py +++ b/py/misc/testing/test_update_website.py @@ -52,7 +52,10 @@ def test_run_tests(): if py.std.sys.platform == "win32": py.test.skip("update_website is not supposed to be run from win32") pkgpath = setup_pkg('update_website_run_tests') - errors = update_website.run_tests(pkgpath, captureouterr=True) + errors = update_website.run_tests(pkgpath, + pkgpath.dirpath().join('apigen'), + captureouterr=True) + print errors assert not errors assert pkgpath.join('../apigen').check(dir=True) assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) @@ -63,6 +66,8 @@ def test_run_tests_failure(): pkgpath = setup_pkg('update_website_run_tests_failure') assert not pkgpath.join('../apigen').check(dir=True) pkgpath.ensure('../apigen', file=True) - errors = update_website.run_tests(pkgpath, captureouterr=True) + errors = update_website.run_tests(pkgpath, + pkgpath.dirpath().join('apigen'), + captureouterr=True) assert errors # some error message