[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 linker
from py.__.apigen import project
from py.__.apigen.tracer.docstorage import pkg_to_dict
def get_documentable_items(pkgdir):
sys.path.insert(0, str(pkgdir.dirpath()))
rootmod = __import__(pkgdir.basename)
return rootmod
return pkg_to_dict(rootmod)
def build(pkgdir, dsa):
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
of the text by taking that of the least indented (filled) line
"""
lines = str.split(linesep)
lines = str.strip().split(linesep)
normalized = []
deindent = None
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:]:
if not line.strip():
line = line.replace('\t', ' ')
stripped = line.strip()
if not stripped:
normalized.append('')
else:
line = line.rstrip()
line = line.replace('\t', ' ')
indent = 0
for c in line:
if c != ' ':
break
indent += 1
rstripped = line.rstrip()
indent = len(rstripped) - len(stripped)
if deindent is None or indent < deindent:
deindent = indent
normalized.append(line)
while normalized[-1] == '':
normalized.pop()
ret = [normalized[0]]
for line in normalized[1:]:
if not line:
@ -182,10 +179,12 @@ def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl,
stylesheeturl=stylesheeturl, scripturls=scripturls)
page.set_content(contentel)
here = py.magic.autopath().dirpath()
style = here.join('style.css').read()
outputpath.join('style.css').write(style)
apijs = here.join('api.js').read()
outputpath.join('api.js').write(apijs)
style = here.join(stylesheeturl.split('/')[-1]).read()
outputpath.join(stylesheeturl.split('/')[-1]).write(style)
for spath in scripturls:
sname = spath.split('/')[-1]
sdata = here.join(sname).read()
outputpath.join(sname).write(sdata)
return page
# the PageBuilder classes take care of producing the docs (using the stuff
@ -281,6 +280,7 @@ class SourcePageBuilder(AbstractPageBuilder):
try:
tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8'))
except UnicodeError:
# XXX we should fix non-ascii support here!!
tag = H.NonPythonSource('no source available (binary file?)')
nav = self.build_navigation(fspath)
return tag, nav
@ -380,7 +380,7 @@ class ApiPageBuilder(AbstractPageBuilder):
csdiv,
)
snippet = H.FunctionDescription(
H.FunctionDef(localname, argdesc),
H.FunctionDef('def %s' % (localname,), argdesc),
H.Docstring(docstring or '*no docstring available*'),
H.div(H.a('show/hide info',
href='#',
@ -684,7 +684,7 @@ class ApiPageBuilder(AbstractPageBuilder):
call_site[0].filename, call_site[0].lineno + 1),
href='#',
onclick="showhideel(getnextsibling(this)); return false;"),
H.div(tbtag, style='display: none')
H.div(tbtag, style='display: none', class_='callstackitem'),
)
return tag

View File

@ -42,12 +42,15 @@
font-weight: bold;
}
body, div, p, h1, h2, h3, h4 {
font-family: Trebuchet MS, Verdana, Arial;
body {
background-color: #FFE;
color: black;
}
body, div, p, h1, h2, h3, h4 {
font-family: Trebuchet MS, Verdana, Arial;
}
a {
color: #006;
text-decoration: none;
@ -107,5 +110,11 @@ a:hover {
border: 1px solid black;
color: black;
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.SomeTestSubClass': ('./sometestsubclass.py',
'SomeTestSubClass'),
'somenamespace': ('./somenamespace.py', '*'),
})
"""))
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():
fs_root, package_name = setup_fs_project('test_get_documentable_items')
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.sub.func']
'main.sub.func', 'somenamespace.baz', 'somenamespace.foo']
def 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
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 storing info about API
"""
@ -117,45 +155,10 @@ class DocStorage(object):
def from_pkg(self, module, keep_frames=False):
self.module = 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(self.get_star_import_tree(base, key))
else:
d[key] = base
self.from_dict(d, keep_frames)
self.from_dict(pkg_to_dict(module), keep_frames)
# XXX
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):
raise NotImplementedError("From module")

View File

@ -6,8 +6,9 @@ import py
import sys
#try:
from py.__.apigen.tracer.tracer import DocStorage, Tracer
from py.__.apigen.tracer.docstorage import DocStorageAccessor
from py.__.apigen.tracer.tracer import Tracer
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.description import FunctionDesc
from py.__.apigen.tracer import model
@ -426,7 +427,7 @@ def setup_pkg_docstorage():
def test_get_initpkg_star_items():
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']
t = Tracer(ds)
t.start_tracing()
@ -446,3 +447,11 @@ def test_get_initpkg_star_items():
assert isinstance(cell, model.SomeInstance)
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']