Skip to content

Commit

Permalink
More coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
jamadden committed May 18, 2017
1 parent 6c4cf9a commit 5a2ca5f
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 61 deletions.
11 changes: 11 additions & 0 deletions src/zope/app/apidoc/browser/README.rst
Expand Up @@ -37,3 +37,14 @@ feel better and does not have all the O-wrap clutter:
<a href="mailto:zope-dev@zope.org">zope-dev@zope.org</a>.
</p>
...

Preferences
-----------

The ``APIDOC`` skin also does the same for editing preference groups:

>>> browser.open('http://localhost/++preferences++apidoc/InterfaceDetails/apidocIndex.html')
>>> print(browser.contents)
<...
<div class="documentation"><p>Preferences for API Docs' Interface Details Screen</p>
...
10 changes: 2 additions & 8 deletions src/zope/app/apidoc/browser/configure.zcml
Expand Up @@ -5,14 +5,8 @@
i18n_domain="zope">

<include package="zope.browserresource" file="meta.zcml" />

<!-- BBB 2006/02/18, to be removed after 12 months -->
<layer
zcml:condition="have deprecatedlayers"
name="apidoc"
interface=".skin.apidoc"
bbb_aware="true"
/>
<include package="zope.browserpage" file="meta.zcml" />
<include package="zope.component" file="meta.zcml" />

<zope:interface
interface=".skin.APIDOC"
Expand Down
1 change: 0 additions & 1 deletion src/zope/app/apidoc/browser/preference.py
Expand Up @@ -13,7 +13,6 @@
##############################################################################
"""API Doc Preference Views
$Id$
"""
__docformat__ = "reStructuredText"

Expand Down
2 changes: 0 additions & 2 deletions src/zope/app/apidoc/classregistry.py
Expand Up @@ -63,8 +63,6 @@ def safe_import(path, default=None):
if module is default and __import_unknown_modules__:
try:
module = __import__(path, {}, {}, ('*',))
except ImportError:
return default
# Some software, we cannot control, might raise all sorts of errors;
# thus catch all exceptions and return the default.
except Exception:
Expand Down
103 changes: 101 additions & 2 deletions src/zope/app/apidoc/codemodule/browser/README.rst
Expand Up @@ -87,6 +87,30 @@ way up to the root, but we just want to go to the root module.
{'name': 'codemodule',
'url': 'http://127.0.0.1/++apidoc++/Code/zope/app/apidoc/codemodule/codemodule'}]

Module Details With Interfaces
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Let's also look at a module that defines interfaces:

>>> _context = traverse(cm, 'zope/app/apidoc/interfaces')
>>> details = browser.module.ModuleDetails(_context, TestRequest())
>>> pprint(details.getInterfaces())
[{'doc': 'Zope 3 API Documentation Module',
'name': 'IDocumentationModule',
'path': 'zope.app.apidoc.interfaces.IDocumentationModule',
'url': 'http://127.0.0.1/++apidoc++/Code/zope/app/apidoc/interfaces/IDocumentationModule'}]

Module Details With Implementation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Let's also look at a module that implements an interface itself:

>>> _context = traverse(cm, 'zope/lifecycleevent')
>>> details = browser.module.ModuleDetails(_context, TestRequest())
>>> pprint(details.getModuleInterfaces())
[{'name': 'IZopeLifecycleEvent',
'path': 'zope.lifecycleevent.interfaces.IZopeLifecycleEvent'}]


Class Details
-------------
Expand Down Expand Up @@ -284,8 +308,7 @@ ZCML files, just a template. The template then uses the directive details to
provide all the view content:

>>> details = browser.zcml.DirectiveDetails()
>>> zcml = traverse(cm,
... 'zope/app/apidoc/codemodule/configure.zcml')
>>> zcml = traverse(cm, 'zope/app/apidoc/codemodule/configure.zcml')
>>> details.context = zcml.rootElement
>>> details.request = TestRequest()
>>> details.__parent__ = details.context
Expand Down Expand Up @@ -392,6 +415,82 @@ Returns a list of all sub-directives:
>>> details.getElements()
[<Directive (u'http://namespaces.zope.org/zope', u'allow')>]

Other Examples
~~~~~~~~~~~~~~

Let's look at sub-directive that has a namespace:

>>> details = browser.zcml.DirectiveDetails()
>>> zcml = traverse(cm, 'zope/app/apidoc/ftesting-base.zcml')
>>> browser_directive = [x for x in zcml.rootElement.subs if x.name[0].endswith('browser')][0]
>>> details.context = browser_directive
>>> details.request = TestRequest()
>>> details.fullTagName()
'browser:menu'

The exact URL will vary depending on what ZCML has been loaded.

>>> details.url()
'http://127.0.0.1/++apidoc++/.../menu/index.html'

Now one that has some tokens:

>>> details = browser.zcml.DirectiveDetails()
>>> zcml = traverse(cm, 'zope/app/apidoc/enabled.zcml')
>>> adapter_directive = [x for x in zcml.rootElement.subs if x.name[1] == 'adapter'][0]
>>> details.context = adapter_directive
>>> details.__parent__ = details.context
>>> details.request = TestRequest()
>>> pprint(details.attributes())
[{'name': 'factory',
'url': 'http://127.0.0.1/++apidoc++/Code/zope/app/apidoc/apidoc/apidocNamespace/index.html',
'value': '.apidoc.apidocNamespace',
'values': []},
{'name': 'provides',
'url': 'http://127.0.0.1/++apidoc++/Interface/zope.traversing.interfaces.ITraversable/index.html',
'value': 'zope.traversing.interfaces.ITraversable',
'values': []},
{'name': 'for', 'url': None, 'value': '*', 'values': []},
{'name': 'name', 'url': None, 'value': 'apidoc', 'values': []}]

Now one with *multiple* tokens:

>>> details = browser.zcml.DirectiveDetails()
>>> zcml = traverse(cm, 'zope/traversing/configure.zcml')
>>> adapter_directive = [x for x in zcml.rootElement.subs if x.name[1] == 'adapter']
>>> adapter_directive = [x for x in adapter_directive if ' ' in x.attrs[(None, 'for')]][0]
>>> details.context = adapter_directive
>>> details.__parent__ = details.context
>>> details.request = TestRequest()
>>> pprint(details.attributes())
[{'name': 'factory',
'url': 'http://127.0.0.1/++apidoc++/Code/zope/traversing/namespace/etc/index.html',
'value': 'zope.traversing.namespace.etc',
'values': []},
{'name': 'provides',
'url': 'http://127.0.0.1/++apidoc++/Interface/zope.traversing.interfaces.ITraversable/index.html',
'value': 'zope.traversing.interfaces.ITraversable',
'values': []},
{'name': 'for',
'url': None,
'value': '* zope.publisher.interfaces.IRequest',
'values': [{'url': None, 'value': '*'},
{'url': 'http://127.0.0.1/++apidoc++/Interface/zope.publisher.interfaces.IRequest/index.html',
'value': 'zope.publisher.interfaces.IRequest'}]},
{'name': 'name', 'url': None, 'value': 'etc', 'values': []}]

And now one that is subdirectives:

>>> details = browser.zcml.DirectiveDetails()
>>> zcml = traverse(cm, 'zope/app/apidoc/browser/configure.zcml')
>>> adapter_directive = [x for x in zcml.rootElement.subs if x.name[1] == 'pages'][0]
>>> details.context = adapter_directive.subs[0]
>>> details.__parent__ = details.context
>>> details.request = TestRequest()
>>> details.url()
'http://127.0.0.1/++apidoc++/.../pages/index.html#page'



The Introspector
----------------
Expand Down
14 changes: 6 additions & 8 deletions src/zope/app/apidoc/codemodule/browser/class_.py
Expand Up @@ -155,11 +155,9 @@ def getDoc(self):
def getConstructor(self):
"""Get info about the constructor, or None if there isn't one."""
attr = self.context.getConstructor()
if attr is None:
return None
attr = removeSecurityProxy(attr)

return {
'signature': getFunctionSignature(attr, ignore_self=True),
'doc': renderText(attr.__doc__ or '', inspect.getmodule(attr)),
}
if attr is not None:
attr = removeSecurityProxy(attr)
return {
'signature': getFunctionSignature(attr, ignore_self=True),
'doc': renderText(attr.__doc__ or '', inspect.getmodule(attr)),
}
12 changes: 4 additions & 8 deletions src/zope/app/apidoc/codemodule/browser/introspector.py
Expand Up @@ -49,7 +49,7 @@ def traverse(self, name, ignore):
# only available in dev-mode.
naked = zope.security.proxy.removeSecurityProxy(self.context)
annotations = annotation.interfaces.IAnnotations(naked)
obj = name and annotations[name] or annotations
obj = annotations[name] if name else annotations
if not IPhysicallyLocatable(obj, False):
obj = location.LocationProxy(
obj, self.context, '++annotations++'+name)
Expand Down Expand Up @@ -168,10 +168,8 @@ def getMethods(self):
val = getattr(obj, name)
if not (inspect.ismethod(val) or inspect.ismethoddescriptor(val)):
continue
if inspect.ismethod(val):
signature = apidoc.utilities.getFunctionSignature(val)
else:
signature = '(...)'

signature = apidoc.utilities.getFunctionSignature(val)

entry = {
'name': name,
Expand Down Expand Up @@ -231,8 +229,6 @@ def getAnnotationsInfo(self):
# so we want to see things that we usually cannot see
naked = zope.security.proxy.removeSecurityProxy(self.context)
annotations = annotation.interfaces.IAnnotations(naked)
if not hasattr(annotations, 'items'):
return
ann = []
for key, value in annotations.items():
ann.append({
Expand All @@ -241,5 +237,5 @@ def getAnnotationsInfo(self):
'value': repr(value),
'value_type': type(value).__name__,
'value_type_link': getTypeLink(type(value))
})
})
return ann
18 changes: 8 additions & 10 deletions src/zope/app/apidoc/codemodule/browser/menu.py
Expand Up @@ -70,11 +70,9 @@ def findClasses(self):
return []
classModule = traverse(self.context, '/++apidoc++')['Code']
classModule.setup()
found = [p for p in classRegistry if path in p]
results = []
for p in classRegistry:
if path not in p:
continue

for p in found:
klass = traverse(classModule, p.replace('.', '/'))
results.append({
'path': p,
Expand Down Expand Up @@ -115,13 +113,13 @@ def findAllClasses(self):
results = []
counter = 0

for p in classRegistry.keys():
for p in list(classRegistry): # Traversing can potentially change the registry
klass = traverse(classModule, p.replace('.', '/'))
results.append(
{'path': p,
'url': absoluteURL(klass, self.request),
'counter': counter
})
results.append({
'path': p,
'url': absoluteURL(klass, self.request),
'counter': counter
})
counter += 1

results.sort(key=_pathgetter)
Expand Down
64 changes: 64 additions & 0 deletions src/zope/app/apidoc/codemodule/browser/tests.py
Expand Up @@ -105,6 +105,70 @@ def testZCMLFileDetailsView(self):
body, '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
basic='mgr:mgrpw')

from BTrees import OOBTree
class TestClass(unittest.TestCase):

layer = APIDocLayer

@unittest.skipIf(OOBTree.OOBTree is OOBTree.OOBTreePy,
"Only in the C implementation")
def test_listClasses_C(self):
from zope.app.apidoc.codemodule.browser.class_ import ClassDetails
from zope.publisher.browser import TestRequest
# BTree items doesn't set a module.

items_class = type(OOBTree.OOBTree({1: 2}).items())

details = ClassDetails()
details.request = TestRequest()
details.context = self.layer.getRootFolder()

info = details._listClasses([items_class])
self.assertIsNone(info[0]['url'], None)

class TestIntrospectorNS(unittest.TestCase):

def _check_namespace(self, kind, context, name):
from zope.app.apidoc.codemodule.browser import introspector
from zope.location import LocationProxy

namespace = getattr(introspector, kind + 'Namespace')
traverser = namespace(context)
obj = traverser.traverse(name, ())
self.assertIsInstance(obj, LocationProxy)

self.assertIs(obj.__parent__, context)
self.assertTrue(obj.__name__.endswith(name))
return traverser, obj

def test_annotations(self):
from zope.annotation.attribute import AttributeAnnotations
anot = AttributeAnnotations(self)
anot['key'] = 42
self._check_namespace('annotations', anot, 'key')

def test_items(self):
self._check_namespace('sequenceItems', ["value"], '0')

def test_mapping(self):
self._check_namespace('mappingItems', {'key': 'value'}, 'key')


class TestIntrospector(unittest.TestCase):
layer = APIDocLayer

classAttr = 1

def testIntrospector(self):
from zope.app.apidoc.codemodule.browser.introspector import Introspector
from zope.publisher.browser import TestRequest

ispect = Introspector(self, TestRequest())
atts = list(ispect.getAttributes())
names = [x['name'] for x in atts]
self.assertIn('classAttr', names)
self.assertNotIn('testAttributes', names)


def test_suite():
return unittest.TestSuite((
Expand Down
12 changes: 6 additions & 6 deletions src/zope/app/apidoc/codemodule/browser/text.py
Expand Up @@ -13,18 +13,18 @@
##############################################################################
"""Function Views
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.app.apidoc.utilities import renderText

class TextFileDetails(object):
"""Represents the details of the text file."""

context = None
request = None

def renderedContent(self):
"""Render the file content to HTML."""
if self.context.path.endswith('.stx'):
format = 'zope.source.stx'
else:
format = 'zope.source.rest'
return renderText(self.context.getContent(), format=format)
ctx = self.context
format = 'zope.source.stx' if ctx.path.endswith('.stx') else 'zope.source.rest'
return renderText(ctx.getContent(), format=format)
6 changes: 3 additions & 3 deletions src/zope/app/apidoc/codemodule/browser/zcml.py
Expand Up @@ -93,12 +93,12 @@ def objectURL(self, value, field, rootURL):
return
try:
isInterface = IInterface.providedBy(obj)
except (AttributeError, TypeError):
except (AttributeError, TypeError): # pragma: no cover
# probably an object that does not like to play nice with the CA
isInterface = False

# The object might be an instance; in this case get a link to the class
if not hasattr(obj, '__name__'):
if not hasattr(obj, '__name__'): # pragma: no cover
obj = getattr(obj, '__class__')
path = getPythonPath(obj)
if isInterface:
Expand Down Expand Up @@ -133,7 +133,7 @@ def attributes(self):
if isinstance(field,
(GlobalObject, GlobalInterface)):
url = self.objectURL(value, field, rootURL)
else:
else: # pragma: no cover
break
attr['values'].append({'value': value, 'url': url})

Expand Down
5 changes: 2 additions & 3 deletions src/zope/app/apidoc/codemodule/class_.py
Expand Up @@ -107,6 +107,5 @@ def getSecurityChecker(self):
def getConstructor(self):
"""See IClassDocumentation."""
init = getattr(self.__klass, '__init__', None)
if not callable(init):
return None
return init
if callable(init):
return init

0 comments on commit 5a2ca5f

Please sign in to comment.