[Checkins] SVN: Sandbox/ulif/zope.introspector/src/zope/introspector/objectinfo.txt Move objectinfo tests to an own doctest.

Uli Fouquet uli at gnufix.de
Wed Jun 18 05:35:59 EDT 2008


Log message for revision 87496:
  Move objectinfo tests to an own doctest.

Changed:
  A   Sandbox/ulif/zope.introspector/src/zope/introspector/objectinfo.txt

-=-
Copied: Sandbox/ulif/zope.introspector/src/zope/introspector/objectinfo.txt (from rev 87495, Sandbox/ulif/zope.introspector/src/zope/introspector/README.txt)
===================================================================
--- Sandbox/ulif/zope.introspector/src/zope/introspector/objectinfo.txt	                        (rev 0)
+++ Sandbox/ulif/zope.introspector/src/zope/introspector/objectinfo.txt	2008-06-18 09:35:58 UTC (rev 87496)
@@ -0,0 +1,299 @@
+zope.introspector
+**********************
+
+:Test-Layer: unit
+
+The `zope.introspector` package provides an extensible framework
+for retrieving 'data' on 'entities'. It makes use of
+grokcore.component for registration of adapters and utilities.
+
+'Entitity' in that respect means everything, that is descriptable by a
+name in Python or everything, that can be passed to a method. In other
+words: if you can pass something to a callable, then the introspector
+should be able to give you some information about it.
+
+'Data' in that respect means a container containing a set of data,
+describing the given entity. The container might contain primitive
+values (like numbers or strings) as well as more complex objects,
+callables etc.
+
+In plain words: Giving a certain object you get a dataset describing
+it.
+
+Support for modification of objects (for instance for debugging
+purposes) is still not implemented. This package also does not include
+viewing components to display the results.
+
+Describing objects
+==================
+
+Simpe objects
+-------------
+
+Simple objects are considered the primitive types provided by Python
+as simple numbers, strings, classes etc. As in a way every thing in
+Python can be seen as a simple object, the ``ObjectInfo`` can handle
+every type of object although the delivered data might lack special
+aspects of more complicated objects.
+
+To handle more comlex objects, there are extended versions of
+``ObjectInfo`` available with zope.introspector that we will discuss
+below and you can write your own ones.
+
+The plain base of all object descriptions generated by
+zope.introspector is the ``ObjectInfo`` class. ObjectInfos can be
+created by passing an object to examine::
+
+   >>> from zope.introspector import ObjectInfo
+   >>> info = ObjectInfo(object())
+   >>> info
+   <zope.introspector...ObjectInfo object at 0x...>
+
+ObjectInfos provide the IObjectInfo interface::
+
+   >>> from zope.introspector.interfaces import IObjectInfo
+   >>> IObjectInfo.providedBy(info)
+   True
+
+We can retrieve basal information about any object with ObjectInfos::
+
+   >>> info.getType()
+   <type 'object'>
+
+Other methods include such which are provided by the Python
+``inspect`` package like ``isModule()``, ``isClass()``,
+``getMembers()``, etc.::
+
+   >>> info.isModule()
+   False
+
+   >>> info.isClass()
+   False
+
+Note, that the implementations of this methods in ``zope.introspector``
+might vary from those of the ``inspect`` package.
+
+As we are speaking about aspects of objects, we can say that every
+Python object is covered by IObjectInfo, as it provides information,
+that is available with every object regardless of as complex it may
+be.
+
+
+More complex objects
+--------------------
+
+Describing more complex objects in terms of ``zope.introspector``
+means to deliver information about certain aspects of those objects,
+that are not covered by the basic ``IObjectInfo`` interface.
+
+As an example we require from the introspector to deliver on request a
+list of local .txt files if an object turns out to be a Python
+package. This will fail with the standard ``ObjectInfo``::
+
+   >>> from zope import introspector
+   >>> info = ObjectInfo(introspector)
+   >>> info.getPackageFiles(filter='.txt')
+   Traceback (most recent call last):
+   ...
+   AttributeError: 'ObjectInfo' object has no attribute 'getPackageFiles'
+
+Fortunately, there is an ``PackageInfo`` available, which gives us
+this information::
+
+   >>> from zope.introspector import PackageInfo
+   >>> info = PackageInfo(introspector)
+   >>> info.getPackageFiles(filter='.txt')
+   ['README.txt']
+
+
+Getting specialized descriptors
+===============================
+
+Overall, the ...Info classes act as descriptors of objects. Doing::
+
+   info = ObjectInfo(obj)
+
+means to generate a descriptor of the object `obj`.
+
+
+Getting a specialist based on the object type
+---------------------------------------------
+
+It would, however, be nice to let the Zope framework look up a
+suitable descriptor or 'specialist' for us more automatically. This is
+in fact possible, using Zope adapters.
+
+To demonstrate the usage of this, we first have to register the
+adapters and utilities of ``zope.introspector``::
+
+   >>> import grokcore.component
+   >>> grokcore.component.testing.grok('zope.introspector')
+
+Now we want to get an descriptor (aka Info object) for a module::
+
+   >>> from zope.introspector.testing import samplemod
+   >>> from zope.introspector.interfaces import IObjectInfo
+   >>> info = IObjectInfo(samplemod)
+   >>> info
+   <zope.introspector.ModuleInfo object at 0x...>
+
+Apparently we got a specialized descriptor here, ``ModuleInfo``
+instead of a plain ``ObjectInfo``.
+
+This is, because modules provide a special type and our framework
+offers a specialized adapter for this type.
+
+We therefore can define 'specialists' for any type of object, as long
+as the Python type is really different.
+
+
+Getting a specialist based on the descriptor interface
+------------------------------------------------------
+
+Things become more complicated when it comes to different kinds of
+objects, that do not differ in the implemented interfaces and Python
+types. 
+
+A use case would be to require different descriptors for packages and
+modules. As packages are modules and both are of the same type for
+Python, we cannot define a specialized adapter based on the type::
+
+   >>> from zope.introspector.tests import subpkg
+   >>> IObectInfo(subpkg)
+   <zope.introspector.ModuleInfo object at 0x...>
+
+That's okay in the sense, that packages are in fact modules, but there
+is an even better descriptor called `PackageInfo`, that provides us
+with data covering the package-specific aspects of `subpkg`. 
+
+To get this specialized package descriptor, we can construct a
+``PackageInfo`` object directly::
+
+   >>> from zope.introspector import PackageInfo
+   >>> info = PackageInfo(subpkg)
+
+or lookup its special interface, for which an adapter should be
+registered:: 
+
+   >>> from zope.introspector.interfaces import IPackageInfo
+   >>> info = IPackageInfo(subpkg)
+   >>> info.getPackageFiles(filter='.txt')
+   ['README.txt']
+
+
+Extending the introspector.core
+===============================
+
+Definition of additional descriptors for zope.introspector can be done
+using adapters and interfaces.
+
+
+Writing an adapter for new types
+--------------------------------
+
+Descriptors might inherit from zope.introspector.ObjectInfo. We
+define a new kind of object, for which we want to provide extra
+information::
+
+   >>> class Cave(object):
+   ...     space=12 # The space in squarefeet
+
+If we look up this class via adapters, we get a type descriptor::
+
+   >>> from zope.introspector.interfaces import IObjectInfo
+   >>> IObjectInfo(Cave)
+   <zope.introspector.TypeInfo object at 0x...>
+
+For objects of this class a class descriptor will be found with stock
+zope.introspector::
+
+   >>> IObjectInfo(Cave())
+   <zope.introspector.ClassInfo object at 0x...>
+
+Let's now build a descriptor, that describes ``Cave`` objects,
+i.e. instances of ``Cave``::
+
+   >>> class CaveInfo(ObjectInfo):
+   ...     grok.component.context(Cave)
+   ...     grok.component.provides(IObjectInfo)
+   ...     def getSpace(self):
+   ...          return context.space
+
+The g.c.provides() statement is not really needed here, because it
+will be inherited from ObjectInfo. But this way we make clear, that
+this class defines an adapter, that adapts Caves to IObjectInfo.
+
+Before we can use the new adapter, we must register it::
+
+   >>> grokcore.component.grok_component('CaveInfo', CaveInfo)
+   True
+
+   >>> IObjectInfo(Cave())
+   <CaveInfo object at 0x...>
+
+Note, that for the ``Cave`` *class* we will still get the same adapter
+as before::
+
+   >>> IObjectInfo(Cave)
+   <zope.introspector.ClassInfo object at 0x...>
+
+
+Writing an interface to cover aspects of an adapted type
+--------------------------------------------------------
+
+Imagine now, that for introspection purposes we want to distuingish
+between plain caves and large caves. Large caves are considered those,
+that have space of more than 20 squarefeet.
+
+Apparently, large caves are also caves which is reflected by the fact,
+that a ``Cave`` object with a ``space`` value greater than 20 is still
+a ``Cave`` object. Large caves and simple caves are of the same type.
+
+This means, that we cannot separate both kinds of cave by looking up
+their types or interfaces. We could indeed define a new interface for
+large caves, but in real life we will see lots of objects for which we
+cannot easily define an interface afterwards. 
+
+Just think of packages and modules. While packages *are* also modules,
+they have some interesting extra data to provide like a list of
+contained .txt files, subpackages etc., which plain modules do not
+have.
+
+In other words: packages are modules that provide an additional
+aspect.
+
+While we cannot easily bind those objects with additional aspects to
+an interface, we can define at least a different interface for the
+expected data. This allows us also to get the additional data by
+looking up adapters.
+
+We define a new interface for large caves::
+
+   >>> from zope.interface import Interface
+   >>> class ILargeCaveInfo(Interface):
+   ...     """Just a marker interface."""
+
+We do not need more than a marker here. This means, that in fact every
+object implements ``ILargeCaveInfo`` but we use it only to register
+and lookup adapters.
+
+We define and register an adapter, that provides special infos about large
+caves:: 
+
+   >>> class LargeCaveInfo(grok.component.Adapter):
+   ...     grok.component.provides(ILargeCaveInfo)
+   ...     grok.component.context(Cave)
+
+   >>> grokcore.component.grok_component(
+   ...    'LargeCaveInfo', LargeCaveInfo)
+   True
+
+Now we can lookup this adapter asking for the new interface
+``ILargeCaveInfo`` instead of ``IObjectInfo``::
+
+   >>> ILargeCaveInfo(cave)
+   <LargeCaveInfo object at 0x...>
+
+We indicate by this call, that we do not want the normal set of
+information regarding caves, but the special information that is
+provided by large caves only.



More information about the Checkins mailing list