[svn r37545] Moved get_star_import_tree out of the DocStorage class, created 'pkg_to_dict'

function in docstorage.py and using that from apigen.py (to later simplify
py.test integration, and to improve testability). Simplified 'deindent()' a
bit, made the wrap_page use the filenames from the stylesheeturl and the
scripturls instead of having them hard-coded, some minor HTML/CSS changes.

--HG--
branch : trunk
This commit is contained in:
guido 2007-01-29 17:11:15 +01:00
parent 06cbe63616
commit fe3a71994a
6 changed files with 84 additions and 61 deletions

View File

@ -10,11 +10,12 @@ import sys
from py.__.apigen import htmlgen from py.__.apigen import htmlgen
from py.__.apigen import linker from py.__.apigen import linker
from py.__.apigen import project from py.__.apigen import project
from py.__.apigen.tracer.docstorage import pkg_to_dict
def get_documentable_items(pkgdir): def get_documentable_items(pkgdir):
sys.path.insert(0, str(pkgdir.dirpath())) sys.path.insert(0, str(pkgdir.dirpath()))
rootmod = __import__(pkgdir.basename) rootmod = __import__(pkgdir.basename)
return rootmod return pkg_to_dict(rootmod)
def build(pkgdir, dsa): def build(pkgdir, dsa):
l = linker.Linker() l = linker.Linker()

View File

@ -19,26 +19,23 @@ def deindent(str, linesep=os.linesep):
line to the side always, and determines the indentation of the rest line to the side always, and determines the indentation of the rest
of the text by taking that of the least indented (filled) line of the text by taking that of the least indented (filled) line
""" """
lines = str.split(linesep) lines = str.strip().split(linesep)
normalized = [] normalized = []
deindent = None deindent = None
normalized.append(lines[0].strip()) normalized.append(lines[0].strip())
# replace tabs with spaces, empty lines that contain spaces only, and
# find out what the smallest indentation is
for line in lines[1:]: for line in lines[1:]:
if not line.strip(): line = line.replace('\t', ' ')
stripped = line.strip()
if not stripped:
normalized.append('') normalized.append('')
else: else:
line = line.rstrip() rstripped = line.rstrip()
line = line.replace('\t', ' ') indent = len(rstripped) - len(stripped)
indent = 0
for c in line:
if c != ' ':
break
indent += 1
if deindent is None or indent < deindent: if deindent is None or indent < deindent:
deindent = indent deindent = indent
normalized.append(line) normalized.append(line)
while normalized[-1] == '':
normalized.pop()
ret = [normalized[0]] ret = [normalized[0]]
for line in normalized[1:]: for line in normalized[1:]:
if not line: if not line:
@ -182,10 +179,12 @@ def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl,
stylesheeturl=stylesheeturl, scripturls=scripturls) stylesheeturl=stylesheeturl, scripturls=scripturls)
page.set_content(contentel) page.set_content(contentel)
here = py.magic.autopath().dirpath() here = py.magic.autopath().dirpath()
style = here.join('style.css').read() style = here.join(stylesheeturl.split('/')[-1]).read()
outputpath.join('style.css').write(style) outputpath.join(stylesheeturl.split('/')[-1]).write(style)
apijs = here.join('api.js').read() for spath in scripturls:
outputpath.join('api.js').write(apijs) sname = spath.split('/')[-1]
sdata = here.join(sname).read()
outputpath.join(sname).write(sdata)
return page return page
# the PageBuilder classes take care of producing the docs (using the stuff # the PageBuilder classes take care of producing the docs (using the stuff
@ -281,6 +280,7 @@ 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
@ -380,7 +380,7 @@ class ApiPageBuilder(AbstractPageBuilder):
csdiv, csdiv,
) )
snippet = H.FunctionDescription( snippet = H.FunctionDescription(
H.FunctionDef(localname, argdesc), H.FunctionDef('def %s' % (localname,), argdesc),
H.Docstring(docstring or '*no docstring available*'), H.Docstring(docstring or '*no docstring available*'),
H.div(H.a('show/hide info', H.div(H.a('show/hide info',
href='#', href='#',
@ -684,7 +684,7 @@ class ApiPageBuilder(AbstractPageBuilder):
call_site[0].filename, call_site[0].lineno + 1), call_site[0].filename, call_site[0].lineno + 1),
href='#', href='#',
onclick="showhideel(getnextsibling(this)); return false;"), onclick="showhideel(getnextsibling(this)); return false;"),
H.div(tbtag, style='display: none') H.div(tbtag, style='display: none', class_='callstackitem'),
) )
return tag return tag

View File

@ -42,12 +42,15 @@
font-weight: bold; font-weight: bold;
} }
body, div, p, h1, h2, h3, h4 { body {
font-family: Trebuchet MS, Verdana, Arial;
background-color: #FFE; background-color: #FFE;
color: black; color: black;
} }
body, div, p, h1, h2, h3, h4 {
font-family: Trebuchet MS, Verdana, Arial;
}
a { a {
color: #006; color: #006;
text-decoration: none; text-decoration: none;
@ -107,5 +110,11 @@ a:hover {
border: 1px solid black; border: 1px solid black;
color: black; color: black;
padding: 1em; padding: 1em;
background-color: white;
}
.callstackitem {
border: 1px solid black;
margin-bottom: 1em;
} }

View File

@ -47,6 +47,7 @@ def setup_fs_project(name):
'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'), 'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'),
'main.SomeTestSubClass': ('./sometestsubclass.py', 'main.SomeTestSubClass': ('./sometestsubclass.py',
'SomeTestSubClass'), 'SomeTestSubClass'),
'somenamespace': ('./somenamespace.py', '*'),
}) })
""")) """))
temp.ensure('pak/test/test_pak.py').write(py.code.Source("""\ temp.ensure('pak/test/test_pak.py').write(py.code.Source("""\
@ -82,9 +83,9 @@ def setup_fs_project(name):
def test_get_documentable_items(): def test_get_documentable_items():
fs_root, package_name = setup_fs_project('test_get_documentable_items') fs_root, package_name = setup_fs_project('test_get_documentable_items')
documentable = apigen.get_documentable_items(fs_root.join(package_name)) documentable = apigen.get_documentable_items(fs_root.join(package_name))
assert sorted(documentable.__package__.exportdefs.keys()) == [ assert sorted(documentable.keys()) == [
'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func',
'main.sub.func'] 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo']
def test_apigen_functional(): def test_apigen_functional():
fs_root, package_name = setup_fs_project('test_apigen_functional') fs_root, package_name = setup_fs_project('test_apigen_functional')

View File

@ -15,6 +15,44 @@ from py.__.apigen.tracer import model
sorted = py.builtin.sorted sorted = py.builtin.sorted
def pkg_to_dict(module):
defs = module.__package__.exportdefs
d = {}
for key, value in defs.iteritems():
chain = key.split('.')
base = module
for elem in chain:
base = getattr(base, elem)
if value[1] == '*':
d.update(get_star_import_tree(base, key))
else:
d[key] = base
return d
def get_star_import_tree(module, modname):
""" deal with '*' entries in an initpkg situation """
ret = {}
modpath = py.path.local(inspect.getsourcefile(module))
pkgpath = module.__package__.getpath()
for objname in dir(module):
if objname.startswith('_'):
continue # also skip __*__ attributes
obj = getattr(module, objname)
if (isinstance(obj, types.ClassType) or
isinstance(obj, types.ObjectType)):
try:
sourcefile_object = py.path.local(
inspect.getsourcefile(obj))
except TypeError:
continue
else:
if sourcefile_object.strpath != modpath.strpath:
# not in this package
continue
dotted_name = '%s.%s' % (modname, objname)
ret[dotted_name] = obj
return ret
class DocStorage(object): class DocStorage(object):
""" Class storing info about API """ Class storing info about API
""" """
@ -117,45 +155,10 @@ class DocStorage(object):
def from_pkg(self, module, keep_frames=False): def from_pkg(self, module, keep_frames=False):
self.module = module self.module = module
defs = module.__package__.exportdefs self.from_dict(pkg_to_dict(module), keep_frames)
d = {}
for key, value in defs.iteritems():
chain = key.split('.')
base = module
for elem in chain:
base = getattr(base, elem)
if value[1] == '*':
d.update(self.get_star_import_tree(base, key))
else:
d[key] = base
self.from_dict(d, keep_frames)
# XXX # XXX
return self return self
def get_star_import_tree(self, module, modname):
""" deal with '*' entries in an initpkg situation """
ret = {}
modpath = py.path.local(inspect.getsourcefile(module))
pkgpath = module.__package__.getpath()
for objname in dir(module):
if objname.startswith('_'):
continue # also skip __*__ attributes
obj = getattr(module, objname)
if (isinstance(obj, types.ClassType) or
isinstance(obj, types.ObjectType)):
try:
sourcefile_object = py.path.local(
inspect.getsourcefile(obj))
except TypeError:
continue
else:
if sourcefile_object.strpath != modpath.strpath:
# not in this package
continue
dotted_name = '%s.%s' % (modname, objname)
ret[dotted_name] = obj
return ret
def from_module(self, func): def from_module(self, func):
raise NotImplementedError("From module") raise NotImplementedError("From module")

View File

@ -6,8 +6,9 @@ import py
import sys import sys
#try: #try:
from py.__.apigen.tracer.tracer import DocStorage, Tracer from py.__.apigen.tracer.tracer import Tracer
from py.__.apigen.tracer.docstorage import DocStorageAccessor from py.__.apigen.tracer.docstorage import DocStorageAccessor, DocStorage, \
get_star_import_tree, pkg_to_dict
from py.__.apigen.tracer.testing.runtest import cut_pyc from py.__.apigen.tracer.testing.runtest import cut_pyc
from py.__.apigen.tracer.description import FunctionDesc from py.__.apigen.tracer.description import FunctionDesc
from py.__.apigen.tracer import model from py.__.apigen.tracer import model
@ -426,7 +427,7 @@ def setup_pkg_docstorage():
def test_get_initpkg_star_items(): def test_get_initpkg_star_items():
pkg, ds = setup_pkg_docstorage() pkg, ds = setup_pkg_docstorage()
sit = ds.get_star_import_tree(pkg.other, 'pkg.other') sit = get_star_import_tree(pkg.other, 'pkg.other')
assert sorted(sit.keys()) == ['pkg.other.baz', 'pkg.other.foo'] assert sorted(sit.keys()) == ['pkg.other.baz', 'pkg.other.foo']
t = Tracer(ds) t = Tracer(ds)
t.start_tracing() t.start_tracing()
@ -446,3 +447,11 @@ def test_get_initpkg_star_items():
assert isinstance(cell, model.SomeInstance) assert isinstance(cell, model.SomeInstance)
assert cell.classdef.cls is desc.pyobj assert cell.classdef.cls is desc.pyobj
def test_pkg_to_dict():
pkg, ds = setup_pkg_docstorage()
assert sorted(pkg_to_dict(pkg).keys()) == ['main.SomeClass',
'main.SomeSubClass',
'main.sub.func',
'other.baz',
'other.foo']