[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:
		
							parent
							
								
									06cbe63616
								
							
						
					
					
						commit
						fe3a71994a
					
				|  | @ -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() | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|      |      | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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') | ||||||
|  |  | ||||||
|  | @ -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") | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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'] | ||||||
|  | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue