[py-svn] r41620 - in py/trunk/py/apigen: . source testing

guido at codespeak.net guido at codespeak.net
Thu Mar 29 13:56:17 CEST 2007


Author: guido
Date: Thu Mar 29 13:56:14 2007
New Revision: 41620

Modified:
   py/trunk/py/apigen/html.py
   py/trunk/py/apigen/htmlgen.py
   py/trunk/py/apigen/linker.py
   py/trunk/py/apigen/source/html.py
   py/trunk/py/apigen/testing/test_apigen_example.py
   py/trunk/py/apigen/testing/test_htmlgen.py
   py/trunk/py/apigen/testing/test_linker.py
Log:
Re-added anchors to the (full) source files, and using them from the API
documentation.


Modified: py/trunk/py/apigen/html.py
==============================================================================
--- py/trunk/py/apigen/html.py	(original)
+++ py/trunk/py/apigen/html.py	Thu Mar 29 13:56:14 2007
@@ -61,6 +61,7 @@
             infoid = 'info_%s' % (localname.replace('.', '_dot_'),)
             docstringid = 'docstring_%s' % (localname.replace('.', '_dot_'),)
             fd = H.FunctionDef(localname, argdesc,
+                               title='click to view details',
                                onclick=('showhideel('
                                         'document.getElementById("%s")); '
                                          % (infoid,)))

Modified: py/trunk/py/apigen/htmlgen.py
==============================================================================
--- py/trunk/py/apigen/htmlgen.py	(original)
+++ py/trunk/py/apigen/htmlgen.py	Thu Mar 29 13:56:14 2007
@@ -18,6 +18,25 @@
 
 REDUCE_CALLSITES = True
 
+def find_method_origin(meth):
+    cls = getattr(meth, 'im_class', None)
+    if cls is None:
+        return None # XXX unknown origin (built-in function or method or sth)
+    name = meth.im_func.func_name
+    origin = cls
+    # XXX old-style classes support required? :|
+    mro = inspect.getmro(cls)
+    for base in mro:
+        m = getattr(base, name, None)
+        if m is None:
+            continue
+        if not hasattr(m, 'im_func'):
+            # builtin
+            return None
+        if m.im_func is meth.im_func:
+            origin = base
+    return origin
+
 def is_navigateable(name):
     return (not is_private(name) and name != '__doc__')
 
@@ -135,10 +154,17 @@
                              source_html.prepare_line([line], tokenizer, enc))
         except py.error.ENOENT:
             # error reading source code, giving up
-            snippet = org
+            snippet = codelines
             break
     return snippet
 
+def enumerate_and_color_module(path, enc):
+    snippet = H.SourceBlock()
+    tokenizer = source_color.Tokenizer(source_color.PythonSchema)
+    for i, text in enumerate(source_html.prepare_module(path, tokenizer, enc)):
+        snippet.add_line(i + 1, text)
+    return snippet
+
 _get_obj_cache = {}
 def get_obj(dsa, pkg, dotted_name):
     full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name)
@@ -258,9 +284,16 @@
         # XXX two reads of the same file here... not very bad (disk caches
         # and such) but also not very nice...
         enc = source_html.get_module_encoding(fspath.strpath)
-        source = fspath.read()
-        sep = get_linesep(source)
-        colored = [enumerate_and_color(source.split(sep), 0, enc)]
+        try:
+            colored = [enumerate_and_color_module(fspath, enc)]
+        except (KeyboardInterrupt, SystemExit):
+            raise
+        except Exception, e:
+            #self.capture.err.writeorg('\ncompilation exception: %s\n' % (e,))
+            # problem building HTML with anchors; let's try without...
+            source = fspath.read()
+            sep = get_linesep(source)
+            colored = [enumerate_and_color(source.split(sep), 0, enc)]
         tag = H.PythonSource(colored)
         nav = self.build_navigation(fspath)
         return tag, nav
@@ -316,7 +349,7 @@
         if fspath.check(ext='.py'):
             try:
                 tag, nav = self.build_python_page(fspath)
-            except (KeyboardInterrupt, SystemError):
+            except (KeyboardInterrupt, SystemExit):
                 raise
             except: # XXX strange stuff going wrong at times... need to fix
                 raise
@@ -398,8 +431,8 @@
             relpath = get_rel_sourcepath(self.projroot, sourcefile, sourcefile)
             text = 'source: %s' % (relpath,)
             if is_in_pkg:
-                href = self.linker.get_lazyhref(sourcefile)
-
+                href = self.linker.get_lazyhref(sourcefile,
+                                                self.get_anchor(func))
         csource = H.SourceSnippet(text, href, colored)
         cslinks = self.build_callsites(dotted_name)
         snippet = H.FunctionDescription(localname, argdesc, docstring,
@@ -432,7 +465,8 @@
             if sourcefile[-1] in ['o', 'c']:
                 sourcefile = sourcefile[:-1]
             sourcelink = H.div(H.a('view source',
-                href=self.linker.get_lazyhref(sourcefile)))
+                href=self.linker.get_lazyhref(sourcefile,
+                                              self.get_anchor(cls))))
 
         snippet = H.ClassDescription(
             # XXX bases HTML
@@ -651,6 +685,10 @@
             return lst
         name, _desc_type, is_degenerated = data
         if not is_degenerated:
+            try:
+                obj = self.dsa.get_obj(name)
+            except KeyError:
+                obj = None
             linktarget = self.linker.get_lazyhref(name)
             lst.append(H.a(str(_type), href=linktarget))
         else:
@@ -695,6 +733,7 @@
     _reg_source = py.std.re.compile(r'([^>]*)<(.*)>')
     def gen_traceback(self, dotted_name, call_site):
         tbtag = H.CallStackDescription()
+        obj = self.dsa.get_obj(dotted_name)
         for frame in call_site:
             lineno = frame.lineno - frame.firstlineno
             source = frame.source
@@ -772,3 +811,20 @@
         self._revcache[dotted_name] = rev
         return rev
 
+    def get_anchor(self, obj):
+        # XXX may not always return the right results...
+        anchor = None
+        if hasattr(obj, 'im_func'):
+            # method
+            origin = find_method_origin(obj)
+            if origin:
+                anchor = '%s.%s' % (origin.__name__,
+                                    obj.im_func.func_name)
+        elif hasattr(obj, 'func_name'):
+            anchor = obj.func_name
+        elif hasattr(obj, '__name__'):
+            anchor = obj.__name__
+        elif hasattr(obj, '__class__'):
+            anchor = obj.__class__.__name__
+        return anchor
+

Modified: py/trunk/py/apigen/linker.py
==============================================================================
--- py/trunk/py/apigen/linker.py	(original)
+++ py/trunk/py/apigen/linker.py	Thu Mar 29 13:56:14 2007
@@ -63,8 +63,11 @@
     def __init__(self):
         self._linkid2target = {}
 
-    def get_lazyhref(self, linkid):
-        return '%s://%s' % (TEMPLINK_PROTO, linkid)
+    def get_lazyhref(self, linkid, anchor=None):
+        href = '%s://%s' % (TEMPLINK_PROTO, linkid)
+        if anchor:
+            href += '#' + anchor
+        return href
 
     def set_link(self, linkid, target):
         assert linkid not in self._linkid2target
@@ -72,13 +75,18 @@
 
     def get_target(self, tempurl, fromlocation=None):
         assert tempurl.startswith('%s://' % (TEMPLINK_PROTO,))
-        linkid = '://'.join(tempurl.split('://')[1:])
+        anchor = None
+        if '#' in tempurl:
+            tempurl, anchor = tempurl.split('#', 1)
+        linkid = tempurl.split('://', 1)[1]
         linktarget = self._linkid2target[linkid]
         if fromlocation is not None:
             linktarget = relpath(fromlocation, linktarget)
+        if anchor is not None:
+            linktarget += '#' + anchor
         return linktarget
 
-    _reg_tempurl = py.std.re.compile('["\'](%s:\/\/[^"\s]*)["\']' % (
+    _reg_tempurl = py.std.re.compile('(["\'])(%s:\/\/[^"\'\s#]*)(["\'#])' % (
                                       TEMPLINK_PROTO,))
     def replace_dirpath(self, dirpath, stoponerrors=True):
         """ replace temporary links in all html files in dirpath and below """
@@ -88,16 +96,19 @@
                 match = self._reg_tempurl.search(html)
                 if not match:
                     break
-                tempurl = match.group(1)
+                tempurl = match.group(2)
+                pre = match.group(1)
+                post = match.group(3)
                 try:
-                    html = html.replace('"' + tempurl + '"',
-                                        '"' + self.get_target(tempurl,
-                                                fpath.relto(dirpath)) + '"')
+                    html = html.replace(match.group(0), pre +
+                                        self.get_target(tempurl,
+                                              fpath.relto(dirpath)) + post)
                 except KeyError:
                     if stoponerrors:
                         raise
-                    html = html.replace('"' + tempurl + '"',
-                                        '"apigen.notfound://%s"' % (tempurl,))
+                    html = html.replace(match.group(0), pre +
+                                        'apigen.notfound://%s' % (tempurl,) +
+                                        post)
             fpath.write(html)
             
 

Modified: py/trunk/py/apigen/source/html.py
==============================================================================
--- py/trunk/py/apigen/source/html.py	(original)
+++ py/trunk/py/apigen/source/html.py	Thu Mar 29 13:56:14 2007
@@ -7,6 +7,10 @@
 from compiler import ast
 import time
 from py.__.apigen.source.color import Tokenizer, PythonSchema
+from py.__.apigen.source.browser import parse_path
+
+class CompilationException(Exception):
+    """ raised when something goes wrong while importing a module """
 
 class HtmlEnchanter(object):
     def __init__(self, mod):
@@ -59,6 +63,30 @@
             ret.append(item)
     return ret
 
+def prepare_module(path, tokenizer, encoding):
+    path = py.path.local(path)
+    try:
+        mod = parse_path(path)
+    except:
+        # XXX don't try to catch SystemExit: it's actually raised by one
+        # of the modules in the py lib on import :(
+        exc, e, tb = py.std.sys.exc_info()
+        del tb
+        raise CompilationException('while compiling %s: %s - %s' % (
+                                    path, e.__class__.__name__, e))
+    lines = [unicode(l, encoding) for l in path.readlines()]
+    
+    enchanter = HtmlEnchanter(mod)
+    ret = []
+    for i, line in enumerate(lines):
+        text = enchanter.enchant_row(i + 1, line)
+        if text == ['']:
+            text = [raw('&#xa0;')]
+        else:
+            text = prepare_line(text, tokenizer, encoding)
+        ret.append(text)
+    return ret
+
 class HTMLDocument(object):
     def __init__(self, encoding, tokenizer=None):
         self.encoding = encoding

Modified: py/trunk/py/apigen/testing/test_apigen_example.py
==============================================================================
--- py/trunk/py/apigen/testing/test_apigen_example.py	(original)
+++ py/trunk/py/apigen/testing/test_apigen_example.py	Thu Mar 29 13:56:14 2007
@@ -198,12 +198,10 @@
         self.apb.build_class_pages(['main.SomeSubClass',
                                     'main.SomeClass'])
         self.apb.build_namespace_pages()
-        # fake some stuff that would be built from other methods
         self.linker.replace_dirpath(self.base, False)
         clsfile = self.base.join('api/main.SomeClass.html')
         assert clsfile.check()
         html = clsfile.read()
-        print html
         run_string_sequence_test(html, [
             'href="../style.css"',
             'href="../apigen_style.css"',
@@ -237,7 +235,8 @@
         self.spb.build_pages(self.fs_root)
         self.linker.replace_dirpath(self.base, False)
         funchtml = self.base.join('api/main.SomeClass.html').read()
-        assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1
+        print funchtml
+        assert funchtml.find('href="../source/pkg/someclass.py.html#SomeClass"') > -1
         _checkhtml(funchtml)
 
     def test_build_namespace_pages(self):
@@ -433,7 +432,8 @@
         assert funcsource.check(file=True)
         html = funcsource.read()
         print html
-        assert ('<span class="alt_keyword">def</span> func(arg1)') in html
+        assert ('<span class="alt_keyword">def</span> '
+                '<a href="#func" name="func">func</a>(arg1)') in html
 
     def test_build_navigation_root(self):
         self.spb.build_pages(self.fs_root)

Modified: py/trunk/py/apigen/testing/test_htmlgen.py
==============================================================================
--- py/trunk/py/apigen/testing/test_htmlgen.py	(original)
+++ py/trunk/py/apigen/testing/test_htmlgen.py	Thu Mar 29 13:56:14 2007
@@ -165,3 +165,30 @@
     assert (htmlgen.get_rel_sourcepath(projpath, py.path.local('<string>')) is
             None)
 
+def test_find_method_origin():
+    class Foo(object):
+        def foo(self):
+            pass
+    class Bar(Foo):
+        def bar(self):
+            pass
+    class Baz(Bar):
+        pass
+    assert htmlgen.find_method_origin(Baz.bar) is Bar
+    assert htmlgen.find_method_origin(Baz.foo) is Foo
+    assert htmlgen.find_method_origin(Bar.bar) is Bar
+    assert htmlgen.find_method_origin(Baz.__init__) is None
+
+def test_find_method_origin_old_style():
+    class Foo:
+        def foo(self):
+            pass
+    class Bar(Foo):
+        def bar(self):
+            pass
+    class Baz(Bar):
+        pass
+    assert htmlgen.find_method_origin(Baz.bar) is Bar
+    assert htmlgen.find_method_origin(Baz.foo) is Foo
+    assert htmlgen.find_method_origin(Bar.bar) is Bar
+

Modified: py/trunk/py/apigen/testing/test_linker.py
==============================================================================
--- py/trunk/py/apigen/testing/test_linker.py	(original)
+++ py/trunk/py/apigen/testing/test_linker.py	Thu Mar 29 13:56:14 2007
@@ -47,6 +47,13 @@
         l.replace_dirpath(temp)
         assert bar.read() == '<a href="baz.html">baz</a>'
 
+    def test_with_anchor(self):
+        linker = TempLinker()
+        temphref = linker.get_lazyhref('py.path.local', 'LocalPath.join')
+        linker.set_link('py.path.local', 'py/path/local.html')
+        relpath = linker.get_target(temphref)
+        assert relpath == 'py/path/local.html#LocalPath.join'
+
 def gen_check(frompath, topath, sep, expected):
     result = relpath(frompath, topath, sep=sep)
     assert result == expected



More information about the pytest-commit mailing list