Logo Search packages:      
Sourcecode: zope-docfindertab version File versions  Download package

analyse.py

# Copyright (C) 2001 by Dr. Dieter Maurer <dieter@handshake.de>
# D-66386 St. Ingbert, Eichendorffstr. 23, Germany
#
#                 All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
# provided that the above copyright notice and this permission
# notice appear in all copies, modified copies and in
# supporting documentation.
# 
# Dieter Maurer DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL Dieter Maurer
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
"""analyse object for methods (names, arguments, documentation) and
permissions."""

# Detail types
DETAIL_PROGRAMMER= 'programmer'
DETAIL_SCRIPTER= 'scripter'
DETAIL_WEB= 'web'

import sys


from funcs import func_prop_tuple
from string import join, replace
import re
from types import IntType, DictType
from AccessControl.SecurityInfo import ClassSecurityInfo, \
     ACCESS_PUBLIC, ACCESS_PRIVATE, ACCESS_NONE
from ExtensionClass import Base


00040 class DocFinder(Base):
  '''determine the documentation of an object.

  Doc is maintained in a two level structure:

   1 classes, the object is build from, in inheritance order

     each class is described by a 'ClassDoc' instance

   2 for each class, the attributes defined by the class
     
     each attribute is described by a 'AttributeDoc' instance.

  The documentation does not include instance level attributes
  (they are too many). However, it does provide
  summary information about access to unprotected attributes
  in the doc for the pseudo class '-- Instance --'.
  This information is not accurate, as the
  '__allow_access_to_unprotected__subobjects__'
  evaluation is not precise.
  '''

  _classDict= None

  _secInfo= ClassSecurityInfo()
  _secInfo.declarePublic('__getitem__','__len__','tpValues', 'tpId')

  def __init__(self, obj, detail_type=DETAIL_SCRIPTER, method_filter= None):
    # encode type
    t= detail_type
    if type(t) == type(''):
      if t == DETAIL_PROGRAMMER: t= 10
      elif t == DETAIL_WEB: t= -10
      else: t= 0

    # print 'DocFinder: ', detail_type, t; sys.stdout.flush()

    # try to get a name
    name= None
    i= getattr(obj,'getId',None) or getattr(obj,'id',None) or getattr(obj,'__name__',None)
    if i is not None:
      if callable(i): i= i()
      name= i
    if name is None: name= '-- Doc --'

    # permanent members
    self._classes= []
    self._type= t
    self._name= name
    self._method_filter=  method_filter and re.compile(method_filter).match

    # temporary members
    self._obj= obj
    self._seenclasses= {}
    self._attributes= {}
    self._check= self._makeUnprotectedChecker()

    c= _getClass(obj)

    ic= ClassDoc('-- Instance --')
    ic._append(AttributeDoc('-- unprotected attributes --',self._attrRoles(),obj= obj))
    self._classes.append(ic)

    self._analyseClassStructure(c)

    # delete temporaries
    del self._obj
    del self._seenclasses
    del self._attributes
    del self._check


00112   def __getitem__(self,k):
    '''allow access by both integer as well as class names.'''
    if type(k) is IntType: return self._classes[k]
    if self._classDict is None:
      cd= self._classDict= {}
      for c in self._classes: cd[c._name]= c
    return self._classDict[k]

  
00121   def __len__(self):
    '''the length of the classes.'''
    return len(self._classes)


00126   def tpValues(self):
    '''tuple of classes for tree display.'''
    return tuple(self._classes)

  def tpId(self):
    return self._name


00134   def _analyseClassStructure(self, c):
    '''analyse class *c* including base classes.'''

    if self._seenclasses.has_key(c): return
    self._seenclasses[c]= None

    self._analyseClass(c)

    for b in c.__bases__:
      self._analyseClassStructure(b)


  def _analyseClass(self, c,
                    _omit= {'__doc__': None, '__module__': None,
                            '__allow_access_to_unprotected_subobjects__': None,
                            }.has_key,
                    _allow= {'__len__': None,
                            '__str__': None,
                            '__getitem__': None,
                            '__call__': None,
                            }.has_key,
                    ):
    '''analyse *c*.'''
    cd= ClassDoc(c.__name__, getattr(c,'__doc__',None),_getLoc(c))
    attributes= self._attributes; seen= attributes.has_key
    check= self._check; o= self._obj; filter= self._method_filter

    for (k,v) in c.__dict__.items():
      if k[-9:] == '__roles__' or _omit(k): continue
      if seen(k): continue
      attributes[k]= None
      if self._type <= 5:
        if k[0] == '_' and not _allow(k): continue
      if filter and not filter(k): continue
      r= getattr(o,k+'__roles__', check(k))
      if self._type <= 0 and _isPrivat(r): continue
      # something interesting
      a= AttributeDoc(k,r,v,o)
      if self._type <= -5 and not a.Doc(): continue
      cd._append(a)

    if self._type <= 5 and not cd: return
    cd._finish()
    self._classes.append(cd)


  def _makeUnprotectedChecker(self):
    roles= getattr(self._obj,'__roles__', ACCESS_PUBLIC)
    allow= getattr(self._obj,'__allow_access_to_unprotected_subobjects__', 0)
    if type(allow) is IntType:
      if not allow: roles= ACCESS_PRIVATE
      def check(name,roles=roles): return roles
    elif type(allow) is DictType:
      def check(name, check=allow.get, roles=roles, priv= ACCESS_PRIVATE):
        if check(name): return roles
        return priv
    else:
      def check(name, obj= self._obj, allow= allow, roles=roles, priv= ACCESS_PRIVATE):
        v= getattr(obj,name)
        if allow(name,v): return roles
        return priv
    return check


  def _attrRoles(self):
    roles= getattr(self._obj,'__roles__', ACCESS_PUBLIC)
    allow= getattr(self._obj,'__allow_access_to_unprotected_subobjects__', 0)
    if type(allow) is IntType:
      if not allow: roles= ACCESS_PRIVATE
    elif type(allow) is DictType: roles= 'Restricted (Dict)'
    else: roles= 'Restricted (Func)'
    return roles


DocFinder._secInfo.apply(DocFinder)


00211 class ClassDoc(Base):
  """the documentation of a class.

  It consists of a 'Name', 'Doc', 'Module' and a list of attributes,
  that can also be accessed via the attribute name.
  """

  _secInfo= ClassSecurityInfo()
  _secInfo.declarePublic('__getitem__','__len__','tpValues', 'tpId', 'Name', 'Doc', 'Module')

  _AttrDict= None

  def __init__(self,name,doc=None,mod=None):
    self._name= name
    self._doc= doc
    self._mod= mod
    self._attrs= []

00229   def __getitem__(self,k):
    '''allow access by both integer as well as attr names.'''
    if type(k) is IntType: return self._attrs[k]
    if self._AttrDict is None:
      cd= self._AttrDict= {}
      for c in self._attrs: cd[c._name]= c
    return self._AttrDict[k]

  
00238   def __len__(self):
    '''the length of the classes.'''
    return len(self._attrs)


00243   def tpValues(self):
    '''tuple of attributes for tree display.'''
    return tuple(self._attrs)

00247   def tpId(self):
    '''use name as id.'''
    return self._name


00252   def Name(self):
    '''the class name.'''
    return self._name

00256   def Doc(self):
    '''the class doc.'''
    return self._doc

00260   def Module(self):
    '''the module the class is defined in.'''
    return self._mod


00265   def _append(self,attr):
    '''append *attr*.'''
    self._attrs.append(attr)


00270   def _finish(self):
    '''finish class definition.'''
    self._attrs.sort(lambda a1,a2, cmp=cmp: cmp(a1._name,a2._name))


ClassDoc._secInfo.apply(ClassDoc)



00279 class AttributeDoc(Base):
  """the documentation of an attribute.

  It consists of a 'Name', 'Roles', 'Args', 'Doc' and 'Type'.
  """

  _secInfo= ClassSecurityInfo()
  _secInfo.declarePublic('tpValues', 'tpId',
                         'Name', 'Roles', 'Args', 'Doc', 'Type', 'DocOrType',
                         'Permission',
                         )

  _knownPermission= 0

  def __init__(self,name,roles,value= None, obj= None):
    if value is not None:
      # determine arguments and documentation, if possible
      arguments= doc= ''
      try:
        (n,a,doc)= func_prop_tuple(value)
        arguments= join(a,', ')
      except: pass
      try: doc= value.__doc__
      except: pass
      type= _getType(value)
    else: doc= arguments= type= ''

    self._name= name
    if roles is ACCESS_PUBLIC: roles= 'public'
    elif roles == ACCESS_PRIVATE: roles= 'private'
    elif roles is ACCESS_NONE: roles= 'none'
    self._roles= roles
    self._arguments= arguments
    self._doc= doc
    self._type= type
    self._obj= obj

00316   def Name(self):
    '''the attribute name'''
    return self._name

00320   def Roles(self):
    '''the attribute roles'''
    return self._roles

00324   def Args(self):
    '''the attribute arguments'''
    return self._arguments

00328   def Doc(self):
    '''the attribute documentation'''
    return self._doc

00332   def Type(self):
    '''the attribute type'''
    return self._type

  def tpValues(self):
    return ()

00339   def tpId(self):
    '''use name as id.'''
    return self._name

00343   def DocOrType(self):
    '''either the Doc (prefered) or the Type.'''
    return self.Doc() or self.Type()

00347   def Permission(self):
    '''return the permission protecting the attribute, 'None' if not directly protected.'''
    if self._knownPermission: return self._permission
    p= None
    if self._obj:
      name= self._name
      if name[:3] == '-- ': name= ''
      p= _lookup(self._obj, name+'__roles__')
      if p is not None:
        try:
          p= replace(p[1]._p[1:-11],'_',' ')
        except: p= '-- explicit --'
    self._permission= p; self._knownPermission= 1
    return p



AttributeDoc._secInfo.apply(AttributeDoc)


def _isPrivat(role):
  return role == ACCESS_PRIVATE or role is ACCESS_NONE


def _getLoc(c):
  '''return location (module) of class *c*.'''
  return getattr(c,'__module__',None)


def _getType(v):
  '''return a nice representation of the *v* type.'''
  tn= type(v).__name__
  if tn == 'instance': tn= '%s %s' % (v.__class__.__name__,tn)
  elif tn == 'instance method': tn= '%s %s' % (v.im_class.__name__,tn)
  return tn


def _getClass(obj):
  '''return the class of *obj*.'''
  return hasattr(obj,'_klass') and obj._klass or obj.__class__


def _lookup(obj,key):
  '''emulate Pythons name lookup; return pair (class,attr) or 'None'.'''
  m= {}
  od= getattr(obj,'__dict__',m)
  v= od.get(key,m)
  if v is not m: return (obj,v)
  v= _lookupClassHierarchy(_getClass(obj),key,m)
  return v


def _lookupClassHierarchy(c,k,m):
  v= c.__dict__.get(k,m)
  if v is not m: return (c,v)
  for c in c.__bases__:
    v= _lookupClassHierarchy(c,k,m)
    if v is not None: return v
  return None

Generated by  Doxygen 1.6.0   Back to index