Source code for zope.app.apidoc.component

##############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Component Inspection Utilities

"""
__docformat__ = 'restructuredtext'
import six
import types
import zope.interface.declarations

from zope.component import getGlobalSiteManager
from zope.component.interfaces import IFactory
from zope.i18nmessageid import ZopeMessageFactory as _
from zope.interface import Interface
from zope.interface.interface import InterfaceClass
from zope.publisher.interfaces import IRequest

from zope.app.apidoc.classregistry import classRegistry
from zope.app.apidoc.utilities import relativizePath, truncateSysPath
from zope.app.apidoc.utilities import getPythonPath, isReferencable, renderText
from zope.app.apidoc.utilitymodule import utilitymodule


SPECIFIC_INTERFACE_LEVEL = 1
EXTENDED_INTERFACE_LEVEL = 2
GENERIC_INTERFACE_LEVEL = 4


def _adapterishRegistrations(registry):
    for registrations in (registry.registeredAdapters,
                          registry.registeredSubscriptionAdapters,
                          registry.registeredHandlers):
        for r in registrations():
            yield r

def _ignore_adapter(reg, withViews=False):
    return (
        # Ignore adapters that have no required interfaces
        not reg.required
        # Ignore views
        or (not withViews and reg.required[-1].isOrExtends(IRequest)))

[docs]def getRequiredAdapters(iface, withViews=False): """Get global adapter registrations where the specified interface is required.""" gsm = getGlobalSiteManager() for reg in _adapterishRegistrations(gsm): if _ignore_adapter(reg, withViews): continue # Only get the adapters for which this interface is required for required_iface in reg.required: if iface.isOrExtends(required_iface): yield reg
[docs]def getProvidedAdapters(iface, withViews=False): """Get global adapter registrations where this interface is provided.""" gsm = getGlobalSiteManager() for reg in _adapterishRegistrations(gsm): if _ignore_adapter(reg, withViews): continue # Only get adapters for which this interface is provided if reg.provided is None or not reg.provided.isOrExtends(iface): continue yield reg
[docs]def filterAdapterRegistrations(regs, iface, level=SPECIFIC_INTERFACE_LEVEL): """Return only those registrations that match the specifed level""" for reg in regs: if level & GENERIC_INTERFACE_LEVEL: for required_iface in reg.required: if required_iface in (Interface, None): yield reg continue if level & EXTENDED_INTERFACE_LEVEL: for required_iface in reg.required: if required_iface is not Interface and \ iface.extends(required_iface): yield reg continue if level & SPECIFIC_INTERFACE_LEVEL: for required_iface in reg.required: if required_iface is iface: yield reg continue
[docs]def getClasses(iface): """Get the classes that implement this interface.""" return classRegistry.getClassesThatImplement(iface)
[docs]def getFactories(iface): """Return the global factory registrations, who will return objects providing this interface.""" gsm = getGlobalSiteManager() for reg in gsm.registeredUtilities(): if reg.provided is not IFactory: continue interfaces = reg.component.getInterfaces() if hasattr(interfaces, 'isOrExtends'): # Single interface interfaces = (interfaces,) for interface in interfaces: if interface.isOrExtends(iface): yield reg break
[docs]def getUtilities(iface): """Return all global utility registrations that provide the interface.""" gsm = getGlobalSiteManager() for reg in gsm.registeredUtilities(): if reg.provided.isOrExtends(iface): yield reg
[docs]def getRealFactory(factory): """Get the real factory. Sometimes the original factory is masked by functions. If the function keeps track of the original factory, use it. """ # Remove all wrappers until none are found anymore. while hasattr(factory, 'factory'): factory = factory.factory # If we have an instance, return its class if not hasattr(factory, '__name__'): return factory.__class__ return factory
[docs]def getParserInfoInfoDictionary(info): """Return a PT-friendly info dictionary for a parser info object.""" return {'file': relativizePath(info.file), 'url': truncateSysPath(info.file).replace('\\', '/'), 'line': info.line, 'eline': info.eline, 'column': info.column, 'ecolumn': info.ecolumn}
[docs]def getInterfaceInfoDictionary(iface): """Return a PT-friendly info dictionary for an interface.""" if isinstance(iface, zope.interface.declarations.Implements): iface = iface.inherit if iface is None: return None return {'module': getattr(iface, '__module__', _('<unknown>')), 'name': getattr(iface, '__name__', _('<unknown>'))}
[docs]def getTypeInfoDictionary(type): """Return a PT-friendly info dictionary for a type.""" path = getPythonPath(type) return {'name': type.__name__, 'module': type.__module__, 'url': isReferencable(path) and path.replace('.', '/') or None}
[docs]def getSpecificationInfoDictionary(spec): """Return an info dictionary for one specification.""" info = {'isInterface': False, 'isType': False} if zope.interface.interfaces.IInterface.providedBy(spec): info.update(getInterfaceInfoDictionary(spec)) info['isInterface'] = True else: info.update(getTypeInfoDictionary(spec.inherit)) info['isType'] = True return info
[docs]def getAdapterInfoDictionary(reg): """Return a PT-friendly info dictionary for an adapter registration.""" factory = getRealFactory(reg.factory) path = getPythonPath(factory) url = None if isReferencable(path): url = path.replace('.', '/') if isinstance(reg.info, six.string_types): doc = reg.info zcml = None else: doc = None zcml = getParserInfoInfoDictionary(reg.info) name = getattr(reg, 'name', u'') name = name.decode('utf-8') if isinstance(name, bytes) else name return { 'provided': getInterfaceInfoDictionary(reg.provided), 'required': [getSpecificationInfoDictionary(iface) for iface in reg.required if iface is not None], 'name': name, 'factory': path, 'factory_url': url, 'doc': doc, 'zcml': zcml }
[docs]def getFactoryInfoDictionary(reg): """Return a PT-friendly info dictionary for a factory.""" factory = reg.component callable = factory # Usually only zope.component.factory.Factory instances have this attribute if IFactory.providedBy(factory) and hasattr(factory, '_callable'): callable = factory._callable elif hasattr(callable, '__class__'): callable = callable.__class__ path = getPythonPath(callable) return {'name': six.text_type(reg.name) or _('<i>no name</i>'), 'title': getattr(factory, 'title', u''), 'description': renderText(getattr(factory, 'description', u''), module=callable.__module__), 'url': isReferencable(path) and path.replace('.', '/') or None}
[docs]def getUtilityInfoDictionary(reg): """Return a PT-friendly info dictionary for a factory.""" component = reg.component # Check whether we have an instance of some custom type or not # Unfortunately, a lot of utilities have a `__name__` attribute, so we # cannot simply check for its absence # TODO: Once we support passive display of instances, this insanity can go # away. if not isinstance(component, (types.MethodType, types.FunctionType, six.class_types, InterfaceClass)): component = getattr(component, '__class__', component) path = getPythonPath(component) # provided interface id iface_id = '%s.%s' % (reg.provided.__module__, reg.provided.getName()) # Determine the URL if isinstance(component, InterfaceClass): url = 'Interface/%s' % path else: url = None if isReferencable(path): url = 'Code/%s' % path.replace('.', '/') return {'name': six.text_type(reg.name) or _('<i>no name</i>'), 'url_name': utilitymodule.encodeName(reg.name or '__noname__'), 'iface_id': iface_id, 'path': path, 'url': url}