Skip to content

Commit

Permalink
Resolve the TODO by implementing withParentAndName for Module. This s…
Browse files Browse the repository at this point in the history
…peeds the tests up some. We could still be lazier if needed.
  • Loading branch information
jamadden committed May 18, 2017
1 parent 45e3028 commit 6c4cf9a
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 22 deletions.
6 changes: 2 additions & 4 deletions src/zope/app/apidoc/browser/apidoc.py
Expand Up @@ -28,16 +28,14 @@ class APIDocumentationView(object):

def getModuleList(self):
"""Get a list of all available documentation modules."""
items = list(self.context.items())
items.sort()
items = sorted(self.context.items())
result = []
for name, module in items:
description = removeSecurityProxy(module.description)
description = translate(description, context=self.request,
default=description)
description = renderText(description, module.__class__.__module__)
if isinstance(description, bytes):
description = description.decode("utf-8")
assert not isinstance(description, bytes)
result.append({'name': name,
'title': module.title,
'description': description})
Expand Down
24 changes: 15 additions & 9 deletions src/zope/app/apidoc/codemodule/browser/menu.py
Expand Up @@ -15,12 +15,15 @@
"""
__docformat__ = 'restructuredtext'
import operator

from zope.traversing.api import traverse
from zope.traversing.browser import absoluteURL

from zope.app.apidoc.classregistry import classRegistry

_pathgetter = operator.itemgetter("path")

class Menu(object):
"""Menu for the Class Documentation Module.
Expand Down Expand Up @@ -66,15 +69,18 @@ def findClasses(self):
if path is None:
return []
classModule = traverse(self.context, '/++apidoc++')['Code']
classModule.setup()
results = []
for p in classRegistry.keys():
if p.find(path) >= 0:
klass = traverse(classModule, p.replace('.', '/'))
results.append(
{'path': p,
'url': absoluteURL(klass, self.request) + '/'
})
results.sort(key=lambda x: x['path'])
for p in classRegistry:
if path not in p:
continue

klass = traverse(classModule, p.replace('.', '/'))
results.append({
'path': p,
'url': absoluteURL(klass, self.request) + '/'
})
results.sort(key=_pathgetter)
return results

def findAllClasses(self):
Expand Down Expand Up @@ -118,5 +124,5 @@ def findAllClasses(self):
})
counter += 1

results.sort(key=lambda x: x['path'])
results.sort(key=_pathgetter)
return results
10 changes: 6 additions & 4 deletions src/zope/app/apidoc/codemodule/codemodule.py
Expand Up @@ -67,19 +67,21 @@ def setup(self):
"""Setup module and class tree."""
if self.__isSetup:
return
self.__isSetup = True
self._children = {}
for name, mod in zope.component.getUtilitiesFor(IAPIDocRootModule):
module = safe_import(mod)
if module is not None:
self._children[name] = Module(self, name, module)
self.__isSetup = True

def withParentAndName(self, parent, name):
located = type(self)()
located.__parent__ = parent
located.__name__ = name
# TODO: This causes the children to get re-computed each time, which
# could be fairly expensive. Can we give them a `withParentAndName`
# too?
self.setup()
located._children = {name: module.withParentAndName(located, name)
for name, module in self._children.items()}
located.__isSetup = True
return located

def getDocString(self):
Expand Down
29 changes: 25 additions & 4 deletions src/zope/app/apidoc/codemodule/module.py
Expand Up @@ -20,6 +20,7 @@
import six

import zope
from zope.proxy import getProxiedObject
from zope.interface import implementer
from zope.interface import providedBy
from zope.interface.interface import InterfaceClass
Expand Down Expand Up @@ -57,16 +58,15 @@ class Module(ReadContainerBase):
"""This class represents a Python module."""

_package = False
_children = None

def __init__(self, parent, name, module, setup=True):
"""Initialize object."""
self.__parent__ = parent
self.__name__ = name
self._module = module
self._children = {}
self._package = False
if setup:
self.__setup()
self.__needsSetup = setup
self.__setup()

def __setup_package(self):
# Detect packages
Expand Down Expand Up @@ -166,6 +166,11 @@ def __setup_classes_and_functions(self):

def __setup(self):
"""Setup the module sub-tree."""
if not self.__needsSetup:
return

self.__needsSetup = False
self._children = {}
self.__setup_package()

zope.deprecation.__show__.off()
Expand All @@ -174,6 +179,22 @@ def __setup(self):
finally:
zope.deprecation.__show__.on()

def withParentAndName(self, parent, name):
located = type(self)(parent, name, self._module, False)
new_children = located._children = {}
for x in self._children.values():
try:
new_child = x.withParentAndName(located, x.__name__)
except AttributeError:
if isinstance(x, LocationProxy):
new_child = LocationProxy(getProxiedObject(x), located, x.__name__)
else:
new_child = LocationProxy(x, located, x.__name__)

new_children[x.__name__] = new_child

return located

def getDocString(self):
"""See IModuleDocumentation."""
return self._module.__doc__
Expand Down
1 change: 1 addition & 0 deletions src/zope/app/apidoc/tests.py
Expand Up @@ -117,6 +117,7 @@ def checkForBrokenLinks(self, orig_response, path,
if href in seen:
continue
seen.add(href)

self.publish(href, basic=basic)

if max_links is not None:
Expand Down
4 changes: 3 additions & 1 deletion src/zope/app/apidoc/utilitymodule/tests.py
Expand Up @@ -42,9 +42,11 @@ def testMenu(self):
# this avoids the deprecation warning for the deprecated
# zope.publisher.interfaces.ILayer interface which get traversed
# as a utility in this test
# This is slow, so we limit the number of links we fetch.
zope.deprecation.__show__.off()
self.checkForBrokenLinks(body, '/++apidoc++/Utility/menu.html',
basic='mgr:mgrpw')
basic='mgr:mgrpw',
max_links=10)
zope.deprecation.__show__.on()

def testUtilityDetailsView(self):
Expand Down

0 comments on commit 6c4cf9a

Please sign in to comment.