[Checkins] SVN: martian/trunk/src/martian/ Move around some documents to make the tutorial the new README.txt

Martijn Faassen faassen at infrae.com
Fri Jun 6 10:53:41 EDT 2008


Log message for revision 87195:
  Move around some documents to make the tutorial the new README.txt
  

Changed:
  D   martian/trunk/src/martian/README.txt
  A   martian/trunk/src/martian/README.txt
  D   martian/trunk/src/martian/core.txt
  A   martian/trunk/src/martian/core.txt
  A   martian/trunk/src/martian/edgecase.txt
  U   martian/trunk/src/martian/tests/test_all.py
  D   martian/trunk/src/martian/tutorial.txt

-=-
Deleted: martian/trunk/src/martian/README.txt
===================================================================
--- martian/trunk/src/martian/README.txt	2008-06-06 14:42:13 UTC (rev 87194)
+++ martian/trunk/src/martian/README.txt	2008-06-06 14:53:41 UTC (rev 87195)
@@ -1,1339 +0,0 @@
-Martian
-=======
-
-"There was so much to grok, so little to grok from." -- Stranger in a
-Strange Land, by Robert A. Heinlein
-
-Martian provides infrastructure for declarative configuration of
-Python code. Martian is especially useful for the construction of
-frameworks that need to provide a flexible plugin
-infrastructure. Martian doesn't actually provide any infrastructure
-for plugin registries and such. Many frameworks have their own, and if
-you need a generic one, you might want to consider
-``zope.component``. Martian just allows you to make the registration
-of such plugins less verbose. 
-
-You can see Martian as doing something that you can also solve with
-metaclasses, with the following advantages:
-
-* the developer of the framework doesn't have to write a lot of ad-hoc
-  metaclasses anymore; instead we offer an infrastructure to make life
-  easier.
-
-* configuration doesn't need to happen at import time, but can happen at
-  program startup time. This also makes configuration more tractable for
-  a developer.
-
-* we don't bother the developer that *uses* the framework with the
-  surprising behavior that metaclasses sometimes bring. The classes
-  the user has to deal with are normal classes.
-
-Why is this package named ``martian``? In the novel "Stranger in a
-Strange Land", the verb *grok* is introduced:
-
-  Grok means to understand so thoroughly that the observer becomes a
-  part of the observed -- to merge, blend, intermarry, lose identity
-  in group experience.
-
-In the context of this package, "grokking" stands for the process of
-deducing declarative configuration actions from Python code. In the
-novel, grokking is originally a concept that comes from the planet
-Mars. Martians *grok*. Since this package helps you grok code, it's
-called Martian.
-
-The ``martian`` package is a spin-off from the `Grok project`_, in the
-context of which this codebase was first developed. While Grok uses
-it, the code is completely independent of Grok.
-
-.. _`Grok project`: http://grok.zope.org
-
-Motivation
-----------
-
-"Deducing declarative configuration actions from Python code" - that
-sounds very abstract. What does it actually mean? In order to explain
-this, let's first look at an example of a simple framework that can be
-*configured* with plugins. We will define a framework for handling
-files based on their extensions::
-
-  >>> class filehandler(FakeModule):
-  ...   import os
-  ...
-  ...   def handle_txt(filepath):
-  ...     return "Text file"
-  ...
-  ...   def handle_xml(filepath):
-  ...     return "XML file"
-  ...
-  ...   extension_handlers = { '.txt': handle_txt, '.xml': handle_xml }
-  ...
-  ...   def handle(filepath):
-  ...      name, ext = os.path.splitext(filepath)
-  ...      return extension_handlers[ext](filepath)
-
-Since normally we cannot create modules in a doctest, we have emulated
-the ``filehandler`` Python module using the ``FakeModule``
-class. Whenever you see ``FakeModule`` subclasses, imagine you're
-looking at a module definition in a ``.py`` file. Now that we have
-defined a module ``filehandler``, we also need to be able to import
-it. To do so we can use a a fake import statement that lets us do
-this::
-
-  >>> filehandler = fake_import(filehandler)
-
-Now let's try the ``handle`` function for a few file types::
-
-  >>> filehandler.handle('test.txt')
-  'Text file'
-  >>> filehandler.handle('test2.xml')
-  'XML file'
-
-File extensions that we do not recognize cause a ``KeyError`` to be
-raised::
-
-  >>> filehandler.handle('image.png')
-  Traceback (most recent call last):
-  ...
-  KeyError: '.png'
-
-We now want to plug into this filehandler framework and provide a
-handler for ``.png`` files. Since we are writing a plugin, we cannot
-change the ``filehandler`` module directly. Let's write an extension
-module instead::
-
-  >>> class pnghandler(FakeModule):
-  ...    def handle_png(filepath):
-  ...        return "PNG file"
-  ...
-  ...    filehandler.extension_handlers['.png'] = handle_png
-  >>> pnghandler = fake_import(pnghandler)
-
-In the extension module, we manipulate the ``extension_handlers``
-dictionary of the ``filehandler`` module and plug in our own
-function. PNG handling works now::
-
-  >>> filehandler.handle('image.png')
-  'PNG file'
-
-The action of registering something into a central registry is also
-called *configuration*. Larger frameworks often offer a lot of points
-where you can configure them: ways to combine its own components with
-components you provide yourself to build a larger application.
-
-Above we plug into our ``extension_handler`` registry using Python
-code. Using separate code to manually hook components into registries
-can get rather cumbersome - each time you write an extension, you also
-need to remember you need to register it. It also poses a maintenance
-risk. It is tempting to start doing fancy things in Python code such
-as conditional configuration, making the configuration state of a
-program hard to understand. Another problem is that doing
-configuration at import time can also lead to unwanted side effects
-during import and ordering problems. It can also make code harder to
-test.
-
-Martian provides a framework that allows configuration to be expressed
-in declarative Python code. These declarations can often be deduced
-from the structure of the code itself. The idea is to make these
-declarations so minimal and easy to read that even extensive
-configuration does not overly burden the programmers working with the
-code. Configuration actions are executed during a separate phase
-("grok time"), not at import time, which makes it easier to reason
-about and easier to test.
-
-Grokkers that grok
-------------------
-
-In this section we define the concept of a ``Grokker``. A ``Grokker``
-is an object that can *grok* objects - execute configuration actions
-pertaining to the grokked object, such as registering it with some
-central registry. Different kinds of grokkers can grok different types
-of objects (instances, classes, functions).
-
-Let's define a Grokker to help us register the file type handler
-functions as seen in our previous example::
-
-  >>> import types
-  >>> from zope.interface import implements
-  >>> import martian
-  >>> class FileTypeGrokker(martian.InstanceGrokker):
-  ...   martian.component(types.FunctionType)
-  ...
-  ...   def grok(self, name, obj, **kw):
-  ...     if not name.startswith('handle_'):
-  ...       return False
-  ...     ext = name.split('_')[1]
-  ...     filehandler.extension_handlers['.' + ext] = obj
-  ...     return True
-
-This ``InstanceGrokker`` allows us to grok instances of a particular
-type (such as functions). We need to define the type of object we're
-looking for with the ``martian.component`` directive. In the ``grok``
-method, we first make sure we only grok functions that have a name
-that starts with ``handle_``. Then we determine the used extension
-from the name and register the funcion in the ``extension_handlers``
-dictionary of the ``filehandler`` module. We return ``True`` if we
-indeed grokked the object.
-
-An instance will provide the IGrokker interface::
-
-  >>> filetype_grokker = FileTypeGrokker()
-  >>> from martian.interfaces import IGrokker
-  >>> IGrokker.providedBy(filetype_grokker)
-  True
-
-Now let's use the grokker to grok a new handle function::
-
-  >>> def handle_jpg(filepath):
-  ...   return "JPG file"
-  >>> filetype_grokker.grok('handle_jpg', handle_jpg)
-  True
-
-After we grokked, we have registered a handler for ``.jpg`` files
-(the extension to register under was deduced from the function name)::
-
-  >>> sorted(filehandler.extension_handlers.keys())
-  ['.jpg', '.png', '.txt', '.xml']
-
-This means now our ``filehandler.handle`` function is now able to
-handle JPG files as well::
-
-  >>> filehandler.handle('image2.jpg')
-  'JPG file'
-
-If we try to grok a function that doesn't start with ``handle_`` in its
-name, nothing will happen::
-
-  >>> def something(filepath):
-  ...   return 'Something'
-  >>> filetype_grokker.grok('something', something)
-  False
-  >>> 'something' in filehandler.extension_handlers
-  False
-
-Grokking a module
------------------
-
-Grokking individual components is useful, but to make Martian really
-useful we need to be able to grok whole modules or packages as well.
-Let's look at a special grokker that can grok a Python module, the
-``ModuleGrokker``.
-
-The idea is that the ``ModuleGrokker`` groks any components in a
-module that it recognizes. A ``ModuleGrokker`` does not work alone. It
-needs to be supplied with one or more grokkers that can grok the
-components to be founded in a module::
-
-  >>> module_grokker = martian.ModuleGrokker()
-  >>> module_grokker.register(filetype_grokker)
-
-We now define a module that defines a few filetype handlers to be
-grokked::
-
-  >>> class lotsofhandlers(FakeModule):
-  ...   def handle_exe(filepath):
-  ...     return "EXE file"
-  ...
-  ...   def handle_ogg(filepath):
-  ...     return "OGG file"
-  ...
-  ...   def handle_svg(filepath):
-  ...     return "SVG file"
-  >>> lotsofhandlers = fake_import(lotsofhandlers)
-
-Let's grok it::
-
-  >>> module_grokker.grok('lotsofhandlers', lotsofhandlers)
-  True
-
-The new registrations are now available::
-
-  >>> sorted(filehandler.extension_handlers.keys())
-  ['.exe', '.jpg', '.ogg', '.png', '.svg', '.txt', '.xml']
-
-The system indeed recognizes them now::
-
-  >>> filehandler.handle('test.ogg')
-  'OGG file'
-  >>> filehandler.handle('test.svg')
-  'SVG file'
-  >>> filehandler.handle('test.exe')
-  'EXE file'
-
-As you can see, with Martian we can now define handlers without ever
-having to register them manually. This allows us to rewrite our
-original module and take out the manual registrations completely::
-
-  >>> class filehandler(FakeModule):
-  ...   import os
-  ...
-  ...   def handle_txt(filepath):
-  ...     return "Text file"
-  ...
-  ...   def handle_xml(filepath):
-  ...     return "XML file"
-  ...
-  ...   extension_handlers = {}
-  ...
-  ...   def handle(filepath):
-  ...      name, ext = os.path.splitext(filepath)
-  ...      return extension_handlers[ext](filepath)
-
-  >>> filehandler = fake_import(filehandler)
-
-Let's use martian to do the registrations for us::
-
-  >>> module_grokker.grok('filehandler', filehandler)
-  True
-  >>> filehandler.handle('test.txt')
-  'Text file'
-
-InstanceGrokker
----------------
-
-We have seen how to grok module-level functions. Let's now grok some
-other kind of instance, a ``Color``::
-
-  >>> class color(FakeModule):
-  ...   class Color(object):
-  ...     def __init__(self, r, g, b):
-  ...       self.r = r
-  ...       self.g = g
-  ...       self.b = b
-  ...     def __repr__(self):
-  ...       return '<Color %s %s %s>' % (self.r, self.g, self.b)
-  ...   all_colors = {}
-  >>> color = fake_import(color)
-
-We now want a grokker that can recognize colors and put them in the
-``all_colors`` dictionary, with the names as the keys, and the color
-object as the values. We can use ``InstanceGrokker`` to construct it::
-
-  >>> class ColorGrokker(martian.InstanceGrokker):
-  ...   martian.component(color.Color)
-  ...   def grok(self, name, obj, **kw):
-  ...     color.all_colors[name] = obj
-  ...     return True
-
-Let's create ``color_grokker`` and grok a color::
-
-  >>> color_grokker = ColorGrokker()
-  >>> black = color.Color(0, 0, 0) # we DO consider black as a color :)
-  >>> color_grokker.grok('black', black)
-  True
-
-It ends up in the ``all_colors`` dictionary::
-
-  >>> color.all_colors
-  {'black': <Color 0 0 0>}
-
-If we put ``color_grokker`` into a ``ModuleGrokker``, we can now grok
-multiple colors in a module::
-
-  >>> Color = color.Color
-  >>> class colors(FakeModule):
-  ...   red = Color(255, 0, 0)
-  ...   green = Color(0, 255, 0)
-  ...   blue = Color(0, 0, 255)
-  ...   white = Color(255, 255, 255)
-  >>> colors = fake_import(colors)
-  >>> colors_grokker = martian.ModuleGrokker()
-  >>> colors_grokker.register(color_grokker)
-  >>> colors_grokker.grok('colors', colors)
-  True
-  >>> sorted(color.all_colors.items())
-  [('black', <Color 0 0 0>),
-   ('blue', <Color 0 0 255>),
-   ('green', <Color 0 255 0>),
-   ('red', <Color 255 0 0>),
-   ('white', <Color 255 255 255>)]
-
-Subclasses of ``Color`` are also grokked::
-
-  >>> class subcolors(FakeModule):
-  ...   class SpecialColor(Color):
-  ...     pass
-  ...   octarine = SpecialColor(-255, 0, -255)
-  >>> subcolors = fake_import(subcolors)
-  >>> colors_grokker.grok('subcolors', subcolors)
-  True
-  >>> 'octarine' in color.all_colors
-  True
-
-MultiInstanceGrokker
---------------------
-
-In the previous section we have created a particular grokker that
-looks for instances of a component class, in this case
-``Color``. Let's introduce another ``InstanceGrokker`` that looks for
-instances of ``Sound``::
-
-  >>> class sound(FakeModule):
-  ...   class Sound(object):
-  ...     def __init__(self, desc):
-  ...       self.desc = desc
-  ...     def __repr__(self):
-  ...       return '<Sound %s>' % (self.desc)
-  ...   all_sounds = {}
-  >>> sound = fake_import(sound)
-
-  >>> class SoundGrokker(martian.InstanceGrokker):
-  ...   martian.component(sound.Sound)
-  ...   def grok(self, name, obj, **kw):
-  ...     sound.all_sounds[name] = obj
-  ...     return True
-  >>> sound_grokker = SoundGrokker()
-
-What if we now want to look for ``Sound`` and ``Color`` instances at
-the same time? We have to use the ``color_grokker`` and
-``sound_grokker`` at the same time, and we can do this with a
-``MultiInstanceGrokker``::
-
-  >>> from martian.core import MultiInstanceGrokker
-  >>> multi_grokker = MultiInstanceGrokker()
-  >>> multi_grokker.register(color_grokker)
-  >>> multi_grokker.register(sound_grokker)
-
-Let's grok a new color with our ``multi_grokker``::
-
-  >>> grey = Color(100, 100, 100)
-  >>> multi_grokker.grok('grey', grey)
-  True
-  >>> 'grey' in color.all_colors
-  True
-
-Let's grok a sound with our ``multi_grokker``::
-
-  >>> moo = sound.Sound('Moo!')
-  >>> multi_grokker.grok('moo', moo)
-  True
-  >>> 'moo' in sound.all_sounds
-  True
-
-We can also grok other objects, but this will have no effect::
-
-  >>> something_else = object()
-  >>> multi_grokker.grok('something_else', something_else)
-  False
-
-Let's put our ``multi_grokker`` in a ``ModuleGrokker``. We can do
-this by passing it explicitly to the ``ModuleGrokker`` factory::
-
-  >>> module_grokker = martian.ModuleGrokker(grokker=multi_grokker)
-
-We can now grok a module for both ``Color`` and ``Sound`` instances::
-
-  >>> Sound = sound.Sound
-  >>> class lightandsound(FakeModule):
-  ...   dark_red = Color(150, 0, 0)
-  ...   scream = Sound('scream')
-  ...   dark_green = Color(0, 150, 0)
-  ...   cheer = Sound('cheer')
-  >>> lightandsound = fake_import(lightandsound)
-  >>> module_grokker.grok('lightandsound', lightandsound)
-  True
-  >>> 'dark_red' in color.all_colors
-  True
-  >>> 'dark_green' in color.all_colors
-  True
-  >>> 'scream' in sound.all_sounds
-  True
-  >>> 'cheer' in sound.all_sounds
-  True
-
-ClassGrokker
-------------
-
-Besides instances we can also grok classes. Let's define an
-application where we register classes representing animals.  Animals
-can be given names using the ``name`` directive::
-
-  >>> from martian.directive import Directive, CLASS, ONCE
-
-  >>> class animal(FakeModule):
-  ...   class name(Directive):
-  ...     scope = CLASS
-  ...     store = ONCE
-  ...
-  ...   class Animal(object):
-  ...     def __repr__(self):
-  ...       return '<Animal %s>' % animal.name.bind().get(self)
-  ...
-  ...   all_animals = {}
-  ...   def create_animal(name):
-  ...     return all_animals[name]()
-  >>> animal = fake_import(animal)
-
-Let's define a grokker that can grok an ``Animal``.  We could either
-implement the ``grok`` method as with ``InstanceGrokkers``, or we can
-rely on the implementation that the baseclass already provides.  In
-the latter case, we just have to declare what directives the grokker
-may want to use on the class and the implement the ``execute`` method::
-
-  >>> class AnimalGrokker(martian.ClassGrokker):
-  ...   martian.component(animal.Animal)
-  ...   martian.directive(animal.name)
-  ...   def execute(self, class_, name, **kw):
-  ...     animal.all_animals[name] = class_
-  ...     return True
-
-Let's test our grokker::
-
-  >>> class Snake(animal.Animal):
-  ...   animal.name('snake')
-  ...
-  >>> animal_grokker = AnimalGrokker()
-  >>> animal_grokker.grok('Snake', Snake)
-  True
-  >>> animal.all_animals.keys()
-  ['snake']
-
-We can create a snake now::
-
-  >>> animal.create_animal('snake')
-  <Animal snake>
-
-Note that we can supply a different default value for the directive
-default when binding the directive to the grokker:
-
-  >>> class AnimalGrokker(AnimalGrokker):
-  ...   martian.directive(animal.name, default='generic animal')
-  ...
-  >>> class Generic(animal.Animal):
-  ...   pass
-  ...
-  >>> animal_grokker = AnimalGrokker()
-  >>> animal_grokker.grok('Generic', Generic)
-  True
-  >>> sorted(animal.all_animals.keys())
-  ['generic animal', 'snake']
-
-Moreover, we can also supply a default factory that may want to
-determine a dynamic default value based on the class that's being
-grokked.  For instance, let's say the default name of an animal should
-the class name converted to lowercase letters::
-
-  >>> def default_animal_name(class_, module, **data):
-  ...   return class_.__name__.lower()
-  ...
-  >>> class AnimalGrokker(AnimalGrokker):
-  ...   martian.directive(animal.name, get_default=default_animal_name)
-  ...
-  >>> class Mouse(animal.Animal):
-  ...   pass
-  ...
-  >>> animal_grokker = AnimalGrokker()
-  >>> animal_grokker.grok('Mouse', Mouse)
-  True
-  >>> sorted(animal.all_animals.keys())
-  ['generic animal', 'mouse', 'snake']
-
-Note that these default value factories will also get the data from
-all directives that are in front of them in the grokker's directive
-list.  For instance, consider the following directive:
-
-  >>> class zoologicalname(animal.name):
-  ...   pass
-  ...
-
-with the following default rule that takes the regular name as the
-default zoological name::
-
-  >>> def default_zoological_name(class_, module, name, **data):
-  ...   return name
-  ...
-  >>> class ZooAnimalGrokker(martian.ClassGrokker):
-  ...   martian.component(animal.Animal)
-  ...   martian.directive(animal.name, get_default=default_animal_name)
-  ...   martian.directive(zoologicalname, get_default=default_zoological_name)
-  ...
-  ...   def execute(self, class_, name, zoologicalname, **kw):
-  ...     print zoologicalname
-  ...     return True
-  ...
-  >>> class Hippopotamus(animal.Animal):
-  ...   pass
-  ...   # No need to use animal.name(), we'll take the class name as default.
-  ...   # The zoological name is the same as well.
-  ...
-
-  >>> zoo_animal_grokker = ZooAnimalGrokker()
-  >>> zoo_animal_grokker.grok('Hippopotamus', Hippopotamus)
-  hippopotamus
-  True
-
-If you pass a non-directive to ``martian.directive``, you get an error::
-
-  >>> class Test(martian.ClassGrokker):
-  ...    martian.directive('foo')
-  Traceback (most recent call last):
-  GrokImportError: The 'directive' directive can only be called with a directive.
-
-MethodGrokker
--------------
-
-A special kind of class grokker is the ``MethodGrokker``.  It inspects
-the class at hand and calls ``execute`` for each *method* the class
-provides.
-
-Consider the following baseclass for circus animals:
-
-  >>> class CircusAnimal(animal.Animal):
-  ...   def begin_show(self):
-  ...     pass
-  ...   def end_show(self):
-  ...      pass
-
-Circus animals define lots of methods which we'll collect using this
-grokker:
-
-  >>> circus_animals = {}
-  >>> from martian import MethodGrokker
-  >>> class CircusAnimalGrokker(MethodGrokker):
-  ...   martian.component(CircusAnimal)
-  ...   def execute(self, class_, method, **kw):
-  ...     circus_animals.setdefault(class_.__name__, []).append(method.__name__)
-  ...     return True
-  ...
-
-Now consider the following circus animals:
-
-  >>> class Monkey(CircusAnimal):
-  ...   def climb(self):
-  ...     pass
-  ...   def _take_dump(self):
-  ...     pass
-  ...
-  >>> class Camel(CircusAnimal):
-  ...   def walk(self):
-  ...     pass
-  ...   def spit(self):
-  ...     pass
-
-  >>> circus_animal_grokker = CircusAnimalGrokker()
-  >>> circus_animal_grokker.grok('Monkey', Monkey)
-  True
-  >>> circus_animal_grokker.grok('Camel', Camel)
-  True
-
-Let's look at the results:
-
-  >>> for circus_animal, methods in sorted(circus_animals.items()):
-  ...     print "%s can %s." % (circus_animal, " and ".join(sorted(methods)))
-  ...
-  Camel can spit and walk.
-  Monkey can climb.
-
-As we see, private methods (those beginning with underscores) have
-been ignored.  Furthermore, methods inherited from the component
-baseclass (in this case ``CircusAnimal``) have also been ignored.
-
-If we wrote a class without any methods, we would encounter an error:
-
-  >>> class Snail(CircusAnimal):
-  ...   pass
-
-  >>> circus_animal_grokker.grok('Snail', Snail)
-  Traceback (most recent call last):
-    ...
-  GrokError: <class 'Snail'> does not define any public
-  methods. Please add methods to this class to enable its
-  registration.
-
-MultiClassGrokker
------------------
-
-We now want to be able to grok the following module and have the
-``Animal`` subclasses (but not the ``Chair`` class, which is not an
-animal) automatically become available::
-
-  >>> class animals(FakeModule):
-  ...   class Elephant(animal.Animal):
-  ...     animal.name('elephant')
-  ...   class Tiger(animal.Animal):
-  ...     animal.name('tiger')
-  ...   class Lion(animal.Animal):
-  ...     animal.name('lion')
-  ...   class Chair(object):
-  ...     animal.name('chair')
-  >>> animals = fake_import(animals)
-
-First we need to wrap our ``AnimalGrokker`` into a ``MultiClassGrokker``::
-
- >>> from martian.core import MultiClassGrokker
- >>> multi_grokker = MultiClassGrokker()
- >>> multi_grokker.register(animal_grokker)
-
-Now let's wrap it into a ``ModuleGrokker`` and grok the module::
-
-  >>> grokker = martian.ModuleGrokker(grokker=multi_grokker)
-  >>> grokker.grok('animals', animals)
-  True
-
-The animals (but not anything else) should have become available::
-
-  >>> sorted(animal.all_animals.keys())
-  ['elephant', 'generic animal', 'lion', 'mouse', 'snake', 'tiger']
-
-We can create animals using their name now::
-
-  >>> animal.create_animal('elephant')
-  <Animal elephant>
-  >>> animal.create_animal('tiger')
-  <Animal tiger>
-
-MultiGrokker
-------------
-
-``MultiInstanceGrokker`` and ``MultiClassGrokker`` can grok instances
-and classes respectively, but a ``MultiInstanceGrokker`` won't work
-correctly if it runs into a class and vice versa. For that we use a
-``MultiGrokker``, which can deal with the full range of objects that
-can be grokked, and skips those it doesn't recognize.
-
-Let's fill a ``MultiGrokker`` with a bunch of grokkers::
-
-  >>> from martian import MultiGrokker
-  >>> multi = MultiGrokker()
-  >>> multi.register(filetype_grokker)
-  >>> multi.register(color_grokker)
-  >>> multi.register(sound_grokker)
-  >>> multi.register(animal_grokker)
-
-Let's try it with some individual objects::
-
-  >>> class Whale(animal.Animal):
-  ...    animal.name('whale')
-  >>> multi.grok('Whale', Whale)
-  True
-  >>> 'whale' in animal.all_animals
-  True
-
-This should have no effect, but not fail::
-
-  >>> my_whale = Whale()
-  >>> multi.grok('my_whale', my_whale)
-  False
-
-Grokked by the ColorGrokker::
-
-  >>> multi.grok('dark_grey', Color(50, 50, 50))
-  True
-  >>> 'dark_grey' in color.all_colors
-  True
-
-Grokked by the SoundGrokker::
-
-  >>> multi.grok('music', Sound('music'))
-  True
-  >>> 'music' in sound.all_sounds
-  True
-
-Not grokked::
-
-  >>> class RockMusic(Sound):
-  ...   pass
-  >>> multi.grok('RockMusic', RockMusic)
-  False
-
-Grokked by SoundGrokker::
-
-  >>> multi.grok('rocknroll', RockMusic('rock n roll'))
-  True
-  >>> 'rocknroll' in sound.all_sounds
-  True
-
-Not grokked::
-
-  >>> class Chair(object):
-  ...   pass
-  >>> multi.grok('Chair', Chair)
-  False
-
-Grokked by ``filetype_grokker``::
-
-  >>> def handle_py(filepath):
-  ...   return "Python file"
-  >>> multi.grok('handle_py', handle_py)
-  True
-  >>> '.py' in filehandler.extension_handlers
-  True
-
-Not grokked:
-
-  >>> def foo():
-  ...   pass
-  >>> multi.grok('foo', foo)
-  False
-
-Not grokked either::
-
-  >>> another = object()
-  >>> multi.grok('another', another)
-  False
-
-Let's make a module which has a mixture between classes and instances,
-some of which can be grokked::
-
-  >>> class mix(FakeModule):
-  ...   # grokked by AnimalGrokker
-  ...   class Whale(animal.Animal):
-  ...      animal.name('whale')
-  ...   # not grokked
-  ...   my_whale = Whale()
-  ...   # grokked by ColorGrokker
-  ...   dark_grey = Color(50, 50, 50)
-  ...   # grokked by SoundGrokker
-  ...   music = Sound('music')
-  ...   # not grokked
-  ...   class RockMusic(Sound):
-  ...      pass
-  ...   # grokked by SoundGrokker
-  ...   rocknroll = RockMusic('rock n roll')
-  ...   # grokked by AnimalGrokker
-  ...   class Dragon(animal.Animal):
-  ...     animal.name('dragon')
-  ...   # not grokked
-  ...   class Chair(object):
-  ...     pass
-  ...   # grokked by filetype_grokker
-  ...   def handle_py(filepath):
-  ...     return "Python file"
-  ...   # not grokked
-  ...   def foo():
-  ...     pass
-  ...   # grokked by AnimalGrokker
-  ...   class SpermWhale(Whale):
-  ...     animal.name('sperm whale')
-  ...   # not grokked
-  ...   another = object()
-  >>> mix = fake_import(mix)
-
-Let's construct a ``ModuleGrokker`` that can grok this module::
-
-  >>> mix_grokker = martian.ModuleGrokker(grokker=multi)
-
-Note that this is actually equivalent to calling ``ModuleGrokker``
-without arguments and then calling ``register`` for the individual
-``ClassGrokker`` and ``InstanceGrokker`` objects.
-
-Before we do the grokking, let's clean up our registration
-dictionaries::
-
-  >>> filehandler.extension_handlers = {}
-  >>> color.all_colors = {}
-  >>> sound.all_sounds = {}
-  >>> animal.all_animals = {}
-
-Now we grok::
-
-  >>> mix_grokker.grok('mix', mix)
-  True
-  >>> sorted(filehandler.extension_handlers.keys())
-  ['.py']
-  >>> sorted(color.all_colors.keys())
-  ['dark_grey']
-  >>> sorted(sound.all_sounds.keys())
-  ['music', 'rocknroll']
-  >>> sorted(animal.all_animals.keys())
-  ['dragon', 'sperm whale', 'whale']
-
-GlobalGrokker
--------------
-
-Sometimes you want to let a grok action happen for each module. The
-grok action could for instance read the globals of a module, or even
-static files associated with the module by name. Let's create a module
-with some global value::
-
-  >>> class g(FakeModule):
-  ...   amount = 50
-  >>> g = fake_import(g)
-
-Now let's create a ``GlobalGrokker`` that reads ``amount`` and stores
-it in the ``read_amount`` dictionary::
-
-  >>> read_amount = {}
-  >>> from martian import GlobalGrokker
-  >>> class AmountGrokker(GlobalGrokker):
-  ...   def grok(self, name, module, **kw):
-  ...     read_amount[None] = module.amount
-  ...     return True
-
-Let's construct a ``ModuleGrokker`` with this ``GlobalGrokker`` registered::
-
-  >>> grokker = martian.ModuleGrokker()
-  >>> grokker.register(AmountGrokker())
-
-Now we grok and should pick up the right value::
-
-  >>> grokker.grok('g', g)
-  True
-  >>> read_amount[None]
-  50
-
-Old-style class support
------------------------
-
-So far we have only grokked either new-style classes or instances of
-new-style classes. It is also possible to grok old-style classes and
-their instances::
-
-  >>> class oldstyle(FakeModule):
-  ...   class Machine:
-  ...     pass
-  ...   all_machines = {}
-  ...   all_machine_instances = {}
-  >>> oldstyle = fake_import(oldstyle)
-
-Let's make a grokker for the old style class::
-
-  >>> class MachineGrokker(martian.ClassGrokker):
-  ...   martian.component(oldstyle.Machine)
-  ...   def grok(self, name, obj, **kw):
-  ...     oldstyle.all_machines[name] = obj
-  ...     return True
-
-And another grokker for old style instances::
-
-  >>> class MachineInstanceGrokker(martian.InstanceGrokker):
-  ...   martian.component(oldstyle.Machine)
-  ...   def grok(self, name, obj, **kw):
-  ...     oldstyle.all_machine_instances[name] = obj
-  ...     return True
-
-The multi grokker should succesfully grok the old-style ``Machine`` class
-and instances of it::
-
-  >>> multi = MultiGrokker()
-  >>> multi.register(MachineGrokker())
-  >>> multi.register(MachineInstanceGrokker())
-  >>> class Robot(oldstyle.Machine):
-  ...   pass
-  >>> multi.grok('Robot', Robot)
-  True
-  >>> oldstyle.all_machines.keys()
-  ['Robot']
-  >>> robot = Robot()
-  >>> multi.grok('robot', robot)
-  True
-  >>> oldstyle.all_machine_instances.keys()
-  ['robot']
-
-Grokking a package
-------------------
-
-A package consists of several sub modules. When grokking a package,
-all the files in the package will be grokked. Let's first create a simple
-grokker for the ``Animal`` class defined by the package::
-
-  >>> from martian.tests.testpackage import animal
-  >>> all_animals = {}
-  >>> class AnimalGrokker(martian.ClassGrokker):
-  ...   martian.component(animal.Animal)
-  ...   def grok(self, name, obj, **kw):
-  ...     all_animals[name] = obj
-  ...     return True
-
-The grokker will collect animals into the ``all_animals`` dictionary.
-
-Let's register this grokker for a ModuleGrokker::
-
-  >>> module_grokker = martian.ModuleGrokker()
-  >>> module_grokker.register(AnimalGrokker())
-
-Now let's grok the whole ``testpackage`` for animals::
-
-  >>> from martian import grok_dotted_name
-  >>> grok_dotted_name('martian.tests.testpackage', grokker=module_grokker)
-
-We should now get some animals::
-
-  >>> sorted(all_animals.keys())
-  ['Animal', 'Bear', 'Dragon', 'Lizard', 'Python', 'SpermWhale', 'Whale']
-
-Preparation and finalization
-----------------------------
-
-Before grokking a module, it may be that we need to do some
-preparation. This preparation can include setting up some parameters
-to pass along to the grokking process, for instance. We can pass
-a ``prepare`` function a the ModuleGrokker::
-
-  >>> class Number(object):
-  ...   def __init__(self, nr):
-  ...     self.nr = nr
-  >>> all_numbers = {}
-  >>> class NumberGrokker(martian.InstanceGrokker):
-  ...  martian.component(Number)
-  ...  def grok(self, name, obj, multiplier, **kw):
-  ...    all_numbers[obj.nr] = obj.nr * multiplier
-  ...    return True
-  >>> def prepare(name, module, kw):
-  ...   kw['multiplier'] = 3
-  >>> module_grokker = martian.ModuleGrokker(prepare=prepare)
-  >>> module_grokker.register(NumberGrokker())
-
-We have created a ``prepare`` function that does one thing: create a
-``multiplier`` parameter that is passed along the grokking
-process. The ``NumberGrokker`` makes use of this to prepare the
-``all_numbers`` dictionary values.
-
-Let's try this with a module::
-
-  >>> class numbers(FakeModule):
-  ...   one = Number(1)
-  ...   two = Number(2)
-  ...   four = Number(4)
-  >>> numbers = fake_import(numbers)
-  >>> module_grokker.grok('numbers', numbers)
-  True
-  >>> sorted(all_numbers.items())
-  [(1, 3), (2, 6), (4, 12)]
-
-You can also optionally register a finalization function, which will
-be run at the end of a module grok::
-
-  >>> def finalize(name, module, kw):
-  ...     all_numbers['finalized'] = True
-  >>> module_grokker = martian.ModuleGrokker(prepare=prepare, finalize=finalize)
-  >>> module_grokker.register(NumberGrokker())
-  >>> all_numbers = {}
-  >>> module_grokker.grok('numbers', numbers)
-  True
-  >>> 'finalized' in all_numbers
-  True
-
-Sanity checking
----------------
-
-Grokkers must return ``True`` if grokking succeeded, or ``False`` if
-it didn't. If they return something else (typically ``None`` as the
-programmer forgot to), the system will raise an error::
-
-  >>> class BrokenGrokker(martian.InstanceGrokker):
-  ...  martian.component(Number)
-  ...  def grok(self, name, obj, **kw):
-  ...    pass
-
-  >>> module_grokker = martian.ModuleGrokker()
-  >>> module_grokker.register(BrokenGrokker())
-  >>> module_grokker.grok('numbers', numbers)
-  Traceback (most recent call last):
-    ...
-  GrokError: <BrokenGrokker object at ...> returns None instead of
-  True or False.
-
-Let's also try this with a GlobalGrokker::
-
-  >>> class MyGrokker(GlobalGrokker):
-  ...   def grok(self, name, module, **kw):
-  ...     return "Foo"
-  >>> module_grokker = martian.ModuleGrokker()
-  >>> module_grokker.register(MyGrokker())
-  >>> module_grokker.grok('numbers', numbers)
-  Traceback (most recent call last):
-    ...
-  GrokError: <MyGrokker object at ...> returns 'Foo' instead of True or False.
-
-Meta Grokkers
--------------
-
-Meta grokkers are grokkers that grok grokkers. This mechanism can be
-used to extend Martian. Let's register a ``ClassMetaGrokker`` that
-looks for subclasses of ``ClassGrokker``::
-
-  >>> from martian.core import MetaGrokker
-  >>> class ClassMetaGrokker(MetaGrokker):
-  ...   martian.component(martian.ClassGrokker)
-  >>> multi_grokker = MultiGrokker()
-  >>> multi_grokker.register(ClassMetaGrokker(multi_grokker))
-
-``multi_grokker`` should now grok subclasses of ``ClassGrokker``, such
-as ``AnimalGrokker``::
-
-  >>> all_animals = {} # clean out animal registry
-  >>> multi_grokker.grok('AnimalGrokker', AnimalGrokker)
-  True
-
-Our multi_grokker should now also be able to grok animals::
-
-  >>> class Woodpecker(animal.Animal):
-  ...   pass
-  >>> multi_grokker.grok('Woodpecker', Woodpecker)
-  True
-
-A ``MetaMultiGrokker`` is a ``MultiGrokker`` that comes preconfigured with
-grokkers for ``ClassGrokker``, ``InstanceGrokker`` and ``GlobalGrokker``::
-
-  >>> from martian import MetaMultiGrokker
-  >>> multi_grokker = MetaMultiGrokker()
-
-It works for ``ClassGrokker``::
-
-  >>> all_animals = {}
-  >>> multi_grokker.grok('AnimalGrokker', AnimalGrokker)
-  True
-  >>> multi_grokker.grok('Woodpecker', Woodpecker)
-  True
-  >>> all_animals
-  {'Woodpecker': <class 'Woodpecker'>}
-
-and for ``InstanceGrokker``::
-
-  >>> color.all_colors = {}
-  >>> multi_grokker.grok('ColorGrokker', ColorGrokker)
-  True
-  >>> multi_grokker.grok('color', Color(255, 0, 0))
-  True
-  >>> color.all_colors
-  {'color': <Color 255 0 0>}
-
-and for ``GlobalGrokker``::
-
-  >>> read_amount = {}
-  >>> multi_grokker.grok('AmountGrokker', AmountGrokker)
-  True
-  >>> grokker.grok('g', g)
-  True
-  >>> read_amount[None]
-  50
-
-We can clear the meta multi grokker::
-
-  >>> multi_grokker.clear()
-
-It won't grok particular classes or instances anymore::
-
-  >>> multi_grokker.grok('Woodpecker', Woodpecker)
-  False
-  >>> multi_grokker.grok('color', Color(255, 0, 0))
-  False
-
-It can still grok grokkers::
-
-  >>> multi_grokker.grok('ColorGrokker', ColorGrokker)
-  True
-
-Executing meta grokkers only once
----------------------------------
-
-In case of ``ClassGrokker`` and all other grokkers that are grokked
-by meta grokkers, we only want the grokking to occur once even if
-the same module (or package) is grokked twice::
-
-  >>> class TestOnce(object):
-  ...   pass
-  >>> executed = []
-  >>> class somemodule(FakeModule):
-  ...   class TestGrokker(martian.ClassGrokker):
-  ...     martian.component(TestOnce)
-  ...     def grok(self, name, obj, **kw):
-  ...        executed.append(name)
-  ...        return True
-  >>> somemodule = fake_import(somemodule)
-  >>> module_grokker = martian.ModuleGrokker(MetaMultiGrokker())
-
-Let's grok the module once::
-
-  >>> module_grokker.grok('somemodule', somemodule)
-  True
-
-Let's grok it twice::
-
-  >>> module_grokker.grok('somemodule', somemodule)
-  True
-
-Even though we have grokked it twice, it is still only registered once. We
-can show this by actually having it grok a ``TestOnce`` subclass::
-
-  >>> class anothermodule(FakeModule):
-  ...   class TestSub(TestOnce):
-  ...      pass
-  >>> anothermodule = fake_import(anothermodule)
-  >>> module_grokker.grok('anothermodule', anothermodule)
-  True
-  >>> executed
-  ['TestSub']
-
-This also works for instance grokkers::
-
-  >>> class TestInstanceOnce(object):
-  ...   pass
-  >>> executed = []
-  >>> class somemodule(FakeModule):
-  ...   class TestGrokker(martian.InstanceGrokker):
-  ...     martian.component(TestInstanceOnce)
-  ...     def grok(self, name, obj, **kw):
-  ...        executed.append(name)
-  ...        return True
-  >>> somemodule = fake_import(somemodule)
-  >>> module_grokker.clear()
-  >>> module_grokker.grok('somemodule', somemodule) # once
-  True
-  >>> module_grokker.grok('somemodule', somemodule) # twice
-  True
-  >>> class anothermodule(FakeModule):
-  ...   test = TestInstanceOnce()
-  >>> anothermodule = fake_import(anothermodule)
-  >>> module_grokker.grok('anothermodule', anothermodule)
-  True
-  >>> executed
-  ['test']
-
-It also works for global grokkers::
-
-  >>> executed = []
-  >>> class somemodule(FakeModule):
-  ...   class TestGrokker(GlobalGrokker):
-  ...     def grok(self, name, obj, **kw):
-  ...       executed.append(name)
-  ...       return True
-  >>> somemodule = fake_import(somemodule)
-  >>> module_grokker.clear()
-  >>> module_grokker.grok('somemodule', somemodule) # once
-  True
-  >>> module_grokker.grok('somemodule', somemodule) # twice
-  True
-
-The second grokking will already make ``somemodule`` grokked::
-
-  >>> executed
-  ['somemodule']
-
-Now let's grok another module::
-
-  >>> class anothermodule(FakeModule):
-  ...   pass
-  >>> anothermodule = fake_import(anothermodule)
-  >>> module_grokker.grok('anothermodule', anothermodule)
-  True
-  >>> executed
-  ['somemodule', 'anothermodule']
-
-Priority
---------
-
-When grokking a module using a ``ModuleGrokker``, grokker execution
-can be determined by their priority. By default, grokkers have a
-priority of ``0``. Let's define two base classes, ``A`` and ``B``,
-which can be grokked::
-
-  >>> class A(object):
-  ...   pass
-
-  >>> class B(object):
-  ...   pass
-
-Let's define a special kind of class grokker that records the order in
-which names get grokked::
-
-  >>> order = []
-  >>> class OrderGrokker(martian.ClassGrokker):
-  ...   def grok(self, name, obj, **kw):
-  ...     order.append(name)
-  ...     return True
-
-Now we define two grokkers for subclasses of ``A`` and ``B``, where
-the ``BGrokker`` has a higher priority::
-
-  >>> class AGrokker(OrderGrokker):
-  ...   martian.component(A)
-  >>> class BGrokker(OrderGrokker):
-  ...   martian.component(B)
-  ...   martian.priority(10)
-
-Let's register these grokkers::
-
-  >>> multi_grokker = MetaMultiGrokker()
-  >>> multi_grokker.grok('AGrokker', AGrokker)
-  True
-  >>> multi_grokker.grok('BGrokker', BGrokker)
-  True
-
-Let's create a module containing ``A`` and ``B`` subclasses::
-
-  >>> class mymodule(FakeModule):
-  ...   class ASub(A):
-  ...     pass
-  ...   class BSub(B):
-  ...     pass
-  >>> mymodule = fake_import(mymodule)
-
-We'll grok it::
-
-  >>> module_grokker = martian.ModuleGrokker(multi_grokker)
-  >>> module_grokker.grok('mymodule', mymodule)
-  True
-
-Since the ``BGrokker`` has a higher priority, we expect the following
-order of grokking::
-
-  >>> order
-  ['BSub', 'ASub']
-
-This also works for GlobalGrokkers. We will define a GlobalGrokker
-that has a higher priority than the default, but lower than B::
-
-  >>> class MyGlobalGrokker(GlobalGrokker):
-  ...   martian.priority(5)
-  ...   def grok(self, name, obj, **kw):
-  ...     order.append(name)
-  ...     return True
-  >>> multi_grokker.grok('MyGlobalGrokker', MyGlobalGrokker)
-  True
-
-We will grok the module again::
-
-  >>> order = []
-  >>> module_grokker.grok('mymodule', mymodule)
-  True
-
-This time, the global grokker should appear after 'BSub' but before 'ASub'::
-
-  >>> order
-  ['BSub', 'mymodule', 'ASub']
-
-
-Module info
------------
-
-In addition to the ``name`` and ``object`` positional arguments,
-grokkers will get also get a ``module_info`` keyword argument.  It is
-an ``IModuleInfo`` object which can be used, for example, to query
-module annotations.  Consider the following grokker:
-
-  >>> from martian.error import GrokError
-  >>> class AnnotationsGrokker(GlobalGrokker):
-  ...   def grok(self, name, module, module_info, **kw):
-  ...       ann = module_info.getAnnotation('some.annotation', None)
-  ...       if ann is None:
-  ...           raise GrokError('Did not find annotation!', module)
-  ...       if ann != 'ME GROK SAY HI':
-  ...           raise GrokError('Wrong annotation!', module)
-  ...       return True
-
-Now let's provide a fake module:
-
-  >>> import new, sys
-  >>> annotations = new.module('annotations')
-  >>> annotations.__file__ = '/fake/module/annotations.py'
-  >>> sys.modules['annotations'] = annotations
-
-Clearly, it can't find the module-level variable yet:
-
-  >>> module_grokker = martian.ModuleGrokker()
-  >>> module_grokker.register(AnnotationsGrokker())
-  >>> import martian
-  >>> martian.grok_dotted_name('annotations', module_grokker)
-  Traceback (most recent call last):
-  ...
-  GrokError: Did not find annotation!
-
-Let's provide the annotation so that the grokker works as expected:
-
-  >>> annotations.__some_annotation__ = 'ME GROK SAY HI'
-  >>> martian.grok_dotted_name('annotations', module_grokker)
-
-Finally clean up:
-
-  >>> del sys.modules['annotations']

Copied: martian/trunk/src/martian/README.txt (from rev 87194, martian/trunk/src/martian/tutorial.txt)
===================================================================
--- martian/trunk/src/martian/README.txt	                        (rev 0)
+++ martian/trunk/src/martian/README.txt	2008-06-06 14:53:41 UTC (rev 87195)
@@ -0,0 +1,411 @@
+Martian
+=======
+
+"There was so much to grok, so little to grok from." -- Stranger in a
+Strange Land, by Robert A. Heinlein
+
+Martian provides infrastructure for declarative configuration of
+Python code. Martian is especially useful for the construction of
+frameworks that need to provide a flexible plugin
+infrastructure. Martian doesn't actually provide any infrastructure
+for plugin registries and such. Many frameworks have their own, and if
+you need a generic one, you might want to consider
+``zope.component``. Martian just allows you to make the registration
+of such plugins less verbose. 
+
+You can see Martian as doing something that you can also solve with
+metaclasses, with the following advantages:
+
+* the developer of the framework doesn't have to write a lot of ad-hoc
+  metaclasses anymore; instead we offer an infrastructure to make life
+  easier.
+
+* configuration doesn't need to happen at import time, but can happen at
+  program startup time. This also makes configuration more tractable for
+  a developer.
+
+* we don't bother the developer that *uses* the framework with the
+  surprising behavior that metaclasses sometimes bring. The classes
+  the user has to deal with are normal classes.
+
+Why is this package named ``martian``? In the novel "Stranger in a
+Strange Land", the verb *grok* is introduced:
+
+  Grok means to understand so thoroughly that the observer becomes a
+  part of the observed -- to merge, blend, intermarry, lose identity
+  in group experience.
+
+In the context of this package, "grokking" stands for the process of
+deducing declarative configuration actions from Python code. In the
+novel, grokking is originally a concept that comes from the planet
+Mars. Martians *grok*. Since this package helps you grok code, it's
+called Martian.
+
+The ``martian`` package is a spin-off from the `Grok project`_, in the
+context of which this codebase was first developed. While Grok uses
+it, the code is completely independent of Grok.
+
+.. _`Grok project`: http://grok.zope.org
+
+Motivation
+----------
+
+"Deducing declarative configuration actions from Python code" - that
+sounds very abstract. What does it actually mean? What is
+configuration?  What is declarative configuration? In order to explain
+this, we'll first take a look at configuration.
+
+Larger frameworks often offer a lot of points where you can modify
+their behavior: ways to combine its own components with components you
+provide yourself to build a larger application. A framework offers
+points where it can be *configured* with plugin code. When you plug
+some code into a plugin point, it results in the updating of some
+registry somewhere with the new plugin. When the framework uses a
+plugin, it will first look it up in the registry. The action of
+registering some component into a registry can be called
+*configuration*.
+
+Let's look at an example framework that offers a plugin point. We
+introduce a very simple framework for plugging in different template
+languages, where each template language uses its own extension. You
+can then supply the framework with the template body and the template
+extension and some data, and render the template.
+
+Let's look at the framework::
+
+  >>> import string
+  >>> class templating(FakeModule):
+  ...
+  ...   class InterpolationTemplate(object):
+  ...      "Use %(foo)s for dictionary interpolation."
+  ...      def __init__(self, text):
+  ...          self.text = text
+  ...      def render(self, **kw):
+  ...          return self.text % kw
+  ...
+  ...   class TemplateStringTemplate(object):
+  ...      "PEP 292 string substitutions."
+  ...      def __init__(self, text):
+  ...          self.template = string.Template(text)
+  ...      def render(self, **kw):
+  ...          return self.template.substitute(**kw)
+  ...
+  ...   # the registry, we plug in the two templating systems right away
+  ...   extension_handlers = { '.txt': InterpolationTemplate, 
+  ...                          '.tmpl': TemplateStringTemplate }
+  ...
+  ...   def render(data, extension, **kw):
+  ...      """Render the template at filepath with arguments.
+  ...  
+  ...      data - the data in the file
+  ...      extension - the extension of the file
+  ...      keyword arguments - variables to interpolate
+  ...
+  ...      In a real framework you could pass in the file path instead of
+  ...      data and extension, but we don't want to open files in our
+  ...      example.
+  ...
+  ...      Returns the rendered template
+  ...      """
+  ...      template = extension_handlers[extension](data)
+  ...      return template.render(**kw)
+
+Since normally we cannot create modules in a doctest, we have emulated
+the ``templating`` Python module using the ``FakeModule``
+class. Whenever you see ``FakeModule`` subclasses, imagine you're
+looking at a module definition in a ``.py`` file. Now that we have
+defined a module ``templating``, we also need to be able to import
+it. To do so we can use a a fake import statement that lets us do
+this::
+
+  >>> templating = fake_import(templating)
+
+Now let's try the ``render`` function for the registered template
+types, to demonstrate that our framework works::
+
+  >>> templating.render('Hello %(name)s!', '.txt', name="world")
+  'Hello world!'
+  >>> templating.render('Hello ${name}!', '.tmpl', name="universe")
+  'Hello universe!'
+
+File extensions that we do not recognize cause a ``KeyError`` to be
+raised::
+
+  >>> templating.render('Hello', '.silly', name="test")
+  Traceback (most recent call last):
+  ...
+  KeyError: '.silly'
+
+We now want to plug into this filehandler framework and provide a
+handler for ``.silly`` files. Since we are writing a plugin, we cannot
+change the ``templating`` module directly. Let's write an extension
+module instead::
+
+  >>> class sillytemplating(FakeModule):
+  ...   class SillyTemplate(object):
+  ...      "Replace {key} with dictionary values."
+  ...      def __init__(self, text):
+  ...          self.text = text
+  ...      def render(self, **kw):
+  ...          text = self.text
+  ...          for key, value in kw.items():
+  ...              text = text.replace('{%s}' % key, value)
+  ...          return text
+  ...
+  ...   templating.extension_handlers['.silly'] = SillyTemplate
+  >>> sillytemplating = fake_import(sillytemplating)
+
+In the extension module, we manipulate the ``extension_handlers``
+dictionary of the ``templating`` module (in normal code we'd need to
+import it first), and plug in our own function. ``.silly`` handling
+works now::
+
+  >>> templating.render('Hello {name}!', '.silly', name="galaxy")
+  'Hello galaxy!'
+
+Above we plug into our ``extension_handler`` registry using Python
+code. Using separate code to manually hook components into registries
+can get rather cumbersome - each time you write a plugin, you also
+need to remember you need to register it. 
+
+Doing template registration in Python code also poses a maintenance
+risk. It is tempting to start doing fancy things in Python code such
+as conditional configuration, making the configuration state of a
+program hard to understand. Another problem is that doing
+configuration at import time can also lead to unwanted side effects
+during import, as well as ordering problems, where you want to import
+something that really needs configuration state in another module that
+is imported later. Finally, it can also make code harder to test, as
+configuration is loaded always when you import the module, even if in
+your test perhaps you don't want it to be.
+
+Martian provides a framework that allows configuration to be expressed
+in declarative Python code. Martian is based on the realization that
+what to configure where can often be deduced from the structure of
+Python code itself, especially when it can be annotated with
+additional declarations. The idea is to make it so easy to write and
+register a plugin so that even extensive configuration does not overly
+burden the developer. 
+
+Configuration actions are executed during a separate phase ("grok
+time"), not at import time, which makes it easier to reason about and
+easier to test.
+
+Configuration the Martian Way
+-----------------------------
+
+Let's now transform the above ``templating`` module and the
+``sillytemplating`` module to use Martian. First we must recognize
+that every template language is configured to work for a particular
+extension. With Martian, we annotate the classes themselves with this
+configuration information. Annotations happen using *directives*,
+which look like function calls in the class body.
+
+Let's create an ``extension`` directive that can take a single string
+as an argument, the file extension to register the template class
+for::
+
+  >>> import martian
+  >>> class extension(martian.Directive):
+  ...   scope = martian.CLASS
+  ...   store = martian.ONCE
+  ...   default = None
+
+We also need a way to easily recognize all template classes. The normal
+pattern for this in Martian is to use a base class, so let's define a
+``Template`` base class::
+
+  >>> class Template(object):
+  ...   pass
+
+We now have enough infrastructure to allow us to change the code to use
+Martian style base class and annotations::
+
+  >>> class templating(FakeModule):
+  ...
+  ...   class InterpolationTemplate(Template):
+  ...      "Use %(foo)s for dictionary interpolation."
+  ...      extension('.txt')
+  ...      def __init__(self, text):
+  ...          self.text = text
+  ...      def render(self, **kw):
+  ...          return self.text % kw
+  ...
+  ...   class TemplateStringTemplate(Template):
+  ...      "PEP 292 string substitutions."
+  ...      extension('.tmpl')
+  ...      def __init__(self, text):
+  ...          self.template = string.Template(text)
+  ...      def render(self, **kw):
+  ...          return self.template.substitute(**kw)
+  ...
+  ...   # the registry, empty to start with
+  ...   extension_handlers = {}
+  ...
+  ...   def render(data, extension, **kw):
+  ...      # this hasn't changed
+  ...      template = extension_handlers[extension](data)
+  ...      return template.render(**kw)
+  >>> templating = fake_import(templating)
+
+As you can see, there have been very few changes:
+
+* we made the template classes inherit from ``Template``.
+
+* we use the ``extension`` directive in the template classes.
+
+* we stopped pre-filling the ``extension_handlers`` dictionary.
+
+So how do we fill the ``extension_handlers`` dictionary with the right
+template languages? Now we can use Martian. We define a *grokker* for
+``Template`` that registers the template classes in the
+``extension_handlers`` registry::
+
+  >>> class meta(FakeModule):   
+  ...   class TemplateGrokker(martian.ClassGrokker):
+  ...     martian.component(Template)
+  ...     martian.directive(extension)
+  ...     def execute(self, class_, extension, **kw):
+  ...       templating.extension_handlers[extension] = class_
+  ...       return True
+  >>> meta = fake_import(meta)
+
+What does this do? A ``ClassGrokker`` has its ``execute`` method
+called for subclasses of what's indicated by the ``martian.component``
+directive. You can also declare what directives a ``ClassGrokker``
+expects on this component by using ``martian.directive()`` (the
+``directive`` directive!) one or more times. 
+
+The ``execute`` method takes the class to be grokked as the first
+argument, and the values of the directives used will be passed in as
+additional parameters into the ``execute`` method. The framework can
+also pass along an arbitrary number of extra keyword arguments during
+the grokking process, so we need to declare ``**kw`` to make sure we
+can handle these.
+
+All our grokkers will be collected in a special Martian-specific
+registry::
+
+  >>> reg = martian.GrokkerRegistry()
+
+We will need to make sure the system is aware of the
+``TemplateGrokker`` defined in the ``meta`` module first, so let's
+register it first. We can do this by simply grokking the ``meta``
+module::
+
+  >>> reg.grok('meta', meta)
+  True
+
+Because ``TemplateGrokker`` is now registered, our registry now knows
+how to grok ``Template`` subclasses. Let's grok the ``templating``
+module::
+
+  >>> reg.grok('templating', templating)
+  True
+
+Let's try the ``render`` function of templating again, to demonstrate
+we have successfully grokked the template classes::
+
+  >>> templating.render('Hello %(name)s!', '.txt', name="world")
+  'Hello world!'
+  >>> templating.render('Hello ${name}!', '.tmpl', name="universe")
+  'Hello universe!'
+
+``.silly`` hasn't been registered yet::
+
+  >>> templating.render('Hello', '.silly', name="test")
+  Traceback (most recent call last):
+  ...
+  KeyError: '.silly'
+
+Let's now register ``.silly`` from an extension module::
+
+  >>> class sillytemplating(FakeModule):
+  ...   class SillyTemplate(Template):
+  ...      "Replace {key} with dictionary values."
+  ...      extension('.silly')
+  ...      def __init__(self, text):
+  ...          self.text = text
+  ...      def render(self, **kw):
+  ...          text = self.text
+  ...          for key, value in kw.items():
+  ...              text = text.replace('{%s}' % key, value)
+  ...          return text
+  >>> sillytemplating = fake_import(sillytemplating)
+
+As you can see, the developer that uses the framework has no need
+anymore to know about ``templating.extension_handlers``. Instead we can
+simply grok the module to have ``SillyTemplate`` be register appropriately::
+
+  >>> reg.grok('sillytemplating', sillytemplating)
+  True
+
+We can now use the ``.silly`` templating engine too::
+
+  >>> templating.render('Hello {name}!', '.silly', name="galaxy")
+  'Hello galaxy!'
+
+Admittedly it is hard to demonstrate Martian well with a small example
+like this. In the end we have actually written more code than in the
+basic framework, after all. But even in this small example, the
+``templating`` and ``sillytemplating`` module have become more
+declarative in nature. The developer that uses the framework will not
+need to know anymore about things like
+``templating.extension_handlers`` or an API to register things
+there. Instead the developer can registering a new template system
+anywhere, as long as he subclasses from ``Template``, and as long as
+his code is grokked by the system.
+
+Finally note how Martian was used to define the ``TemplateGrokker`` as
+well. In this way Martian can use itself to extend itself.
+
+Grokking instances
+------------------
+
+Above we've seen how you can grok classes. Martian also supplies a way
+to grok instances. This is less common in typical frameworks, and has
+the drawback that no class-level directives can be used, but can still
+be useful.
+
+Let's imagine a case where we have a zoo framework with an ``Animal``
+class, and we want to track instances of it::
+
+  >>> class Animal(object):
+  ...   def __init__(self, name):
+  ...     self.name = name
+  >>> class zoo(FakeModule):
+  ...   horse = Animal('horse')
+  ...   chicken = Animal('chicken')
+  ...   elephant = Animal('elephant')
+  ...   lion = Animal('lion')
+  ...   animals = {}
+  >>> zoo = fake_import(zoo)
+ 
+We define an ``InstanceGrokker`` subclass to grok ``Animal`` instances::
+
+  >>> class meta(FakeModule):   
+  ...   class AnimalGrokker(martian.InstanceGrokker):
+  ...     martian.component(Animal)
+  ...     def execute(self, instance, **kw):
+  ...       zoo.animals[instance.name] = instance
+  ...       return True
+  >>> meta = fake_import(meta)
+
+Let's create a new registry with the ``AnimalGrokker`` in it::
+   
+  >>> reg = martian.GrokkerRegistry()
+  >>> reg.grok('meta', meta)
+  True
+
+We can now grok the ``zoo`` module::
+
+  >>> reg.grok('zoo', zoo)
+  True
+
+The animals will now be in the ``animals`` dictionary::
+
+  >>> sorted(zoo.animals.items())
+  [('chicken', <Animal object at ...>), 
+   ('elephant', <Animal object at ...>), 
+   ('horse', <Animal object at ...>), 
+   ('lion', <Animal object at ...>)]

Deleted: martian/trunk/src/martian/core.txt
===================================================================
--- martian/trunk/src/martian/core.txt	2008-06-06 14:42:13 UTC (rev 87194)
+++ martian/trunk/src/martian/core.txt	2008-06-06 14:53:41 UTC (rev 87195)
@@ -1,47 +0,0 @@
-(Edge-case) tests of Martian core components
-============================================
-
-ModuleGrokker ignores values set by directives
-----------------------------------------------
-
-Consider the following module-level directive:
-
-  >>> import martian
-  >>> class store(martian.Directive):
-  ...     scope = martian.MODULE
-  ...     store = martian.ONCE
-  ...
-  >>> store.__module__ = 'somethingelse'  # just so that it isn't __builtin__
-
-Now let's look at a module that contains a simple function and a call
-to the directive defined above:
-
-  >>> class module_with_directive(FakeModule):
-  ...     fake_module = True
-  ...
-  ...     def some_function():
-  ...         return 11
-  ...
-  ...     store(some_function)
-  ...
-  >>> module_with_directive = fake_import(module_with_directive)
-
-Now imagine we have the following grokker for functions:
-
-  >>> import types
-  >>> class FunctionGrokker(martian.InstanceGrokker):
-  ...     martian.component(types.FunctionType)
-  ...     def grok(self, name, obj, **kw):
-  ...         print name, obj()
-  ...         return True
-  ...
-  >>> module_grokker = martian.ModuleGrokker()
-  >>> module_grokker.register(FunctionGrokker())
-
-and let it loose on the module, we see that it will only find functions
-set by regular variable assignment, not the ones stored by the
-directive:
-
-  >>> module_grokker.grok('module_with_directive', module_with_directive)
-  some_function 11
-  True

Copied: martian/trunk/src/martian/core.txt (from rev 87194, martian/trunk/src/martian/README.txt)
===================================================================
--- martian/trunk/src/martian/core.txt	                        (rev 0)
+++ martian/trunk/src/martian/core.txt	2008-06-06 14:53:41 UTC (rev 87195)
@@ -0,0 +1,1339 @@
+Martian
+=======
+
+"There was so much to grok, so little to grok from." -- Stranger in a
+Strange Land, by Robert A. Heinlein
+
+Martian provides infrastructure for declarative configuration of
+Python code. Martian is especially useful for the construction of
+frameworks that need to provide a flexible plugin
+infrastructure. Martian doesn't actually provide any infrastructure
+for plugin registries and such. Many frameworks have their own, and if
+you need a generic one, you might want to consider
+``zope.component``. Martian just allows you to make the registration
+of such plugins less verbose. 
+
+You can see Martian as doing something that you can also solve with
+metaclasses, with the following advantages:
+
+* the developer of the framework doesn't have to write a lot of ad-hoc
+  metaclasses anymore; instead we offer an infrastructure to make life
+  easier.
+
+* configuration doesn't need to happen at import time, but can happen at
+  program startup time. This also makes configuration more tractable for
+  a developer.
+
+* we don't bother the developer that *uses* the framework with the
+  surprising behavior that metaclasses sometimes bring. The classes
+  the user has to deal with are normal classes.
+
+Why is this package named ``martian``? In the novel "Stranger in a
+Strange Land", the verb *grok* is introduced:
+
+  Grok means to understand so thoroughly that the observer becomes a
+  part of the observed -- to merge, blend, intermarry, lose identity
+  in group experience.
+
+In the context of this package, "grokking" stands for the process of
+deducing declarative configuration actions from Python code. In the
+novel, grokking is originally a concept that comes from the planet
+Mars. Martians *grok*. Since this package helps you grok code, it's
+called Martian.
+
+The ``martian`` package is a spin-off from the `Grok project`_, in the
+context of which this codebase was first developed. While Grok uses
+it, the code is completely independent of Grok.
+
+.. _`Grok project`: http://grok.zope.org
+
+Motivation
+----------
+
+"Deducing declarative configuration actions from Python code" - that
+sounds very abstract. What does it actually mean? In order to explain
+this, let's first look at an example of a simple framework that can be
+*configured* with plugins. We will define a framework for handling
+files based on their extensions::
+
+  >>> class filehandler(FakeModule):
+  ...   import os
+  ...
+  ...   def handle_txt(filepath):
+  ...     return "Text file"
+  ...
+  ...   def handle_xml(filepath):
+  ...     return "XML file"
+  ...
+  ...   extension_handlers = { '.txt': handle_txt, '.xml': handle_xml }
+  ...
+  ...   def handle(filepath):
+  ...      name, ext = os.path.splitext(filepath)
+  ...      return extension_handlers[ext](filepath)
+
+Since normally we cannot create modules in a doctest, we have emulated
+the ``filehandler`` Python module using the ``FakeModule``
+class. Whenever you see ``FakeModule`` subclasses, imagine you're
+looking at a module definition in a ``.py`` file. Now that we have
+defined a module ``filehandler``, we also need to be able to import
+it. To do so we can use a a fake import statement that lets us do
+this::
+
+  >>> filehandler = fake_import(filehandler)
+
+Now let's try the ``handle`` function for a few file types::
+
+  >>> filehandler.handle('test.txt')
+  'Text file'
+  >>> filehandler.handle('test2.xml')
+  'XML file'
+
+File extensions that we do not recognize cause a ``KeyError`` to be
+raised::
+
+  >>> filehandler.handle('image.png')
+  Traceback (most recent call last):
+  ...
+  KeyError: '.png'
+
+We now want to plug into this filehandler framework and provide a
+handler for ``.png`` files. Since we are writing a plugin, we cannot
+change the ``filehandler`` module directly. Let's write an extension
+module instead::
+
+  >>> class pnghandler(FakeModule):
+  ...    def handle_png(filepath):
+  ...        return "PNG file"
+  ...
+  ...    filehandler.extension_handlers['.png'] = handle_png
+  >>> pnghandler = fake_import(pnghandler)
+
+In the extension module, we manipulate the ``extension_handlers``
+dictionary of the ``filehandler`` module and plug in our own
+function. PNG handling works now::
+
+  >>> filehandler.handle('image.png')
+  'PNG file'
+
+The action of registering something into a central registry is also
+called *configuration*. Larger frameworks often offer a lot of points
+where you can configure them: ways to combine its own components with
+components you provide yourself to build a larger application.
+
+Above we plug into our ``extension_handler`` registry using Python
+code. Using separate code to manually hook components into registries
+can get rather cumbersome - each time you write an extension, you also
+need to remember you need to register it. It also poses a maintenance
+risk. It is tempting to start doing fancy things in Python code such
+as conditional configuration, making the configuration state of a
+program hard to understand. Another problem is that doing
+configuration at import time can also lead to unwanted side effects
+during import and ordering problems. It can also make code harder to
+test.
+
+Martian provides a framework that allows configuration to be expressed
+in declarative Python code. These declarations can often be deduced
+from the structure of the code itself. The idea is to make these
+declarations so minimal and easy to read that even extensive
+configuration does not overly burden the programmers working with the
+code. Configuration actions are executed during a separate phase
+("grok time"), not at import time, which makes it easier to reason
+about and easier to test.
+
+Grokkers that grok
+------------------
+
+In this section we define the concept of a ``Grokker``. A ``Grokker``
+is an object that can *grok* objects - execute configuration actions
+pertaining to the grokked object, such as registering it with some
+central registry. Different kinds of grokkers can grok different types
+of objects (instances, classes, functions).
+
+Let's define a Grokker to help us register the file type handler
+functions as seen in our previous example::
+
+  >>> import types
+  >>> from zope.interface import implements
+  >>> import martian
+  >>> class FileTypeGrokker(martian.InstanceGrokker):
+  ...   martian.component(types.FunctionType)
+  ...
+  ...   def grok(self, name, obj, **kw):
+  ...     if not name.startswith('handle_'):
+  ...       return False
+  ...     ext = name.split('_')[1]
+  ...     filehandler.extension_handlers['.' + ext] = obj
+  ...     return True
+
+This ``InstanceGrokker`` allows us to grok instances of a particular
+type (such as functions). We need to define the type of object we're
+looking for with the ``martian.component`` directive. In the ``grok``
+method, we first make sure we only grok functions that have a name
+that starts with ``handle_``. Then we determine the used extension
+from the name and register the funcion in the ``extension_handlers``
+dictionary of the ``filehandler`` module. We return ``True`` if we
+indeed grokked the object.
+
+An instance will provide the IGrokker interface::
+
+  >>> filetype_grokker = FileTypeGrokker()
+  >>> from martian.interfaces import IGrokker
+  >>> IGrokker.providedBy(filetype_grokker)
+  True
+
+Now let's use the grokker to grok a new handle function::
+
+  >>> def handle_jpg(filepath):
+  ...   return "JPG file"
+  >>> filetype_grokker.grok('handle_jpg', handle_jpg)
+  True
+
+After we grokked, we have registered a handler for ``.jpg`` files
+(the extension to register under was deduced from the function name)::
+
+  >>> sorted(filehandler.extension_handlers.keys())
+  ['.jpg', '.png', '.txt', '.xml']
+
+This means now our ``filehandler.handle`` function is now able to
+handle JPG files as well::
+
+  >>> filehandler.handle('image2.jpg')
+  'JPG file'
+
+If we try to grok a function that doesn't start with ``handle_`` in its
+name, nothing will happen::
+
+  >>> def something(filepath):
+  ...   return 'Something'
+  >>> filetype_grokker.grok('something', something)
+  False
+  >>> 'something' in filehandler.extension_handlers
+  False
+
+Grokking a module
+-----------------
+
+Grokking individual components is useful, but to make Martian really
+useful we need to be able to grok whole modules or packages as well.
+Let's look at a special grokker that can grok a Python module, the
+``ModuleGrokker``.
+
+The idea is that the ``ModuleGrokker`` groks any components in a
+module that it recognizes. A ``ModuleGrokker`` does not work alone. It
+needs to be supplied with one or more grokkers that can grok the
+components to be founded in a module::
+
+  >>> module_grokker = martian.ModuleGrokker()
+  >>> module_grokker.register(filetype_grokker)
+
+We now define a module that defines a few filetype handlers to be
+grokked::
+
+  >>> class lotsofhandlers(FakeModule):
+  ...   def handle_exe(filepath):
+  ...     return "EXE file"
+  ...
+  ...   def handle_ogg(filepath):
+  ...     return "OGG file"
+  ...
+  ...   def handle_svg(filepath):
+  ...     return "SVG file"
+  >>> lotsofhandlers = fake_import(lotsofhandlers)
+
+Let's grok it::
+
+  >>> module_grokker.grok('lotsofhandlers', lotsofhandlers)
+  True
+
+The new registrations are now available::
+
+  >>> sorted(filehandler.extension_handlers.keys())
+  ['.exe', '.jpg', '.ogg', '.png', '.svg', '.txt', '.xml']
+
+The system indeed recognizes them now::
+
+  >>> filehandler.handle('test.ogg')
+  'OGG file'
+  >>> filehandler.handle('test.svg')
+  'SVG file'
+  >>> filehandler.handle('test.exe')
+  'EXE file'
+
+As you can see, with Martian we can now define handlers without ever
+having to register them manually. This allows us to rewrite our
+original module and take out the manual registrations completely::
+
+  >>> class filehandler(FakeModule):
+  ...   import os
+  ...
+  ...   def handle_txt(filepath):
+  ...     return "Text file"
+  ...
+  ...   def handle_xml(filepath):
+  ...     return "XML file"
+  ...
+  ...   extension_handlers = {}
+  ...
+  ...   def handle(filepath):
+  ...      name, ext = os.path.splitext(filepath)
+  ...      return extension_handlers[ext](filepath)
+
+  >>> filehandler = fake_import(filehandler)
+
+Let's use martian to do the registrations for us::
+
+  >>> module_grokker.grok('filehandler', filehandler)
+  True
+  >>> filehandler.handle('test.txt')
+  'Text file'
+
+InstanceGrokker
+---------------
+
+We have seen how to grok module-level functions. Let's now grok some
+other kind of instance, a ``Color``::
+
+  >>> class color(FakeModule):
+  ...   class Color(object):
+  ...     def __init__(self, r, g, b):
+  ...       self.r = r
+  ...       self.g = g
+  ...       self.b = b
+  ...     def __repr__(self):
+  ...       return '<Color %s %s %s>' % (self.r, self.g, self.b)
+  ...   all_colors = {}
+  >>> color = fake_import(color)
+
+We now want a grokker that can recognize colors and put them in the
+``all_colors`` dictionary, with the names as the keys, and the color
+object as the values. We can use ``InstanceGrokker`` to construct it::
+
+  >>> class ColorGrokker(martian.InstanceGrokker):
+  ...   martian.component(color.Color)
+  ...   def grok(self, name, obj, **kw):
+  ...     color.all_colors[name] = obj
+  ...     return True
+
+Let's create ``color_grokker`` and grok a color::
+
+  >>> color_grokker = ColorGrokker()
+  >>> black = color.Color(0, 0, 0) # we DO consider black as a color :)
+  >>> color_grokker.grok('black', black)
+  True
+
+It ends up in the ``all_colors`` dictionary::
+
+  >>> color.all_colors
+  {'black': <Color 0 0 0>}
+
+If we put ``color_grokker`` into a ``ModuleGrokker``, we can now grok
+multiple colors in a module::
+
+  >>> Color = color.Color
+  >>> class colors(FakeModule):
+  ...   red = Color(255, 0, 0)
+  ...   green = Color(0, 255, 0)
+  ...   blue = Color(0, 0, 255)
+  ...   white = Color(255, 255, 255)
+  >>> colors = fake_import(colors)
+  >>> colors_grokker = martian.ModuleGrokker()
+  >>> colors_grokker.register(color_grokker)
+  >>> colors_grokker.grok('colors', colors)
+  True
+  >>> sorted(color.all_colors.items())
+  [('black', <Color 0 0 0>),
+   ('blue', <Color 0 0 255>),
+   ('green', <Color 0 255 0>),
+   ('red', <Color 255 0 0>),
+   ('white', <Color 255 255 255>)]
+
+Subclasses of ``Color`` are also grokked::
+
+  >>> class subcolors(FakeModule):
+  ...   class SpecialColor(Color):
+  ...     pass
+  ...   octarine = SpecialColor(-255, 0, -255)
+  >>> subcolors = fake_import(subcolors)
+  >>> colors_grokker.grok('subcolors', subcolors)
+  True
+  >>> 'octarine' in color.all_colors
+  True
+
+MultiInstanceGrokker
+--------------------
+
+In the previous section we have created a particular grokker that
+looks for instances of a component class, in this case
+``Color``. Let's introduce another ``InstanceGrokker`` that looks for
+instances of ``Sound``::
+
+  >>> class sound(FakeModule):
+  ...   class Sound(object):
+  ...     def __init__(self, desc):
+  ...       self.desc = desc
+  ...     def __repr__(self):
+  ...       return '<Sound %s>' % (self.desc)
+  ...   all_sounds = {}
+  >>> sound = fake_import(sound)
+
+  >>> class SoundGrokker(martian.InstanceGrokker):
+  ...   martian.component(sound.Sound)
+  ...   def grok(self, name, obj, **kw):
+  ...     sound.all_sounds[name] = obj
+  ...     return True
+  >>> sound_grokker = SoundGrokker()
+
+What if we now want to look for ``Sound`` and ``Color`` instances at
+the same time? We have to use the ``color_grokker`` and
+``sound_grokker`` at the same time, and we can do this with a
+``MultiInstanceGrokker``::
+
+  >>> from martian.core import MultiInstanceGrokker
+  >>> multi_grokker = MultiInstanceGrokker()
+  >>> multi_grokker.register(color_grokker)
+  >>> multi_grokker.register(sound_grokker)
+
+Let's grok a new color with our ``multi_grokker``::
+
+  >>> grey = Color(100, 100, 100)
+  >>> multi_grokker.grok('grey', grey)
+  True
+  >>> 'grey' in color.all_colors
+  True
+
+Let's grok a sound with our ``multi_grokker``::
+
+  >>> moo = sound.Sound('Moo!')
+  >>> multi_grokker.grok('moo', moo)
+  True
+  >>> 'moo' in sound.all_sounds
+  True
+
+We can also grok other objects, but this will have no effect::
+
+  >>> something_else = object()
+  >>> multi_grokker.grok('something_else', something_else)
+  False
+
+Let's put our ``multi_grokker`` in a ``ModuleGrokker``. We can do
+this by passing it explicitly to the ``ModuleGrokker`` factory::
+
+  >>> module_grokker = martian.ModuleGrokker(grokker=multi_grokker)
+
+We can now grok a module for both ``Color`` and ``Sound`` instances::
+
+  >>> Sound = sound.Sound
+  >>> class lightandsound(FakeModule):
+  ...   dark_red = Color(150, 0, 0)
+  ...   scream = Sound('scream')
+  ...   dark_green = Color(0, 150, 0)
+  ...   cheer = Sound('cheer')
+  >>> lightandsound = fake_import(lightandsound)
+  >>> module_grokker.grok('lightandsound', lightandsound)
+  True
+  >>> 'dark_red' in color.all_colors
+  True
+  >>> 'dark_green' in color.all_colors
+  True
+  >>> 'scream' in sound.all_sounds
+  True
+  >>> 'cheer' in sound.all_sounds
+  True
+
+ClassGrokker
+------------
+
+Besides instances we can also grok classes. Let's define an
+application where we register classes representing animals.  Animals
+can be given names using the ``name`` directive::
+
+  >>> from martian.directive import Directive, CLASS, ONCE
+
+  >>> class animal(FakeModule):
+  ...   class name(Directive):
+  ...     scope = CLASS
+  ...     store = ONCE
+  ...
+  ...   class Animal(object):
+  ...     def __repr__(self):
+  ...       return '<Animal %s>' % animal.name.bind().get(self)
+  ...
+  ...   all_animals = {}
+  ...   def create_animal(name):
+  ...     return all_animals[name]()
+  >>> animal = fake_import(animal)
+
+Let's define a grokker that can grok an ``Animal``.  We could either
+implement the ``grok`` method as with ``InstanceGrokkers``, or we can
+rely on the implementation that the baseclass already provides.  In
+the latter case, we just have to declare what directives the grokker
+may want to use on the class and the implement the ``execute`` method::
+
+  >>> class AnimalGrokker(martian.ClassGrokker):
+  ...   martian.component(animal.Animal)
+  ...   martian.directive(animal.name)
+  ...   def execute(self, class_, name, **kw):
+  ...     animal.all_animals[name] = class_
+  ...     return True
+
+Let's test our grokker::
+
+  >>> class Snake(animal.Animal):
+  ...   animal.name('snake')
+  ...
+  >>> animal_grokker = AnimalGrokker()
+  >>> animal_grokker.grok('Snake', Snake)
+  True
+  >>> animal.all_animals.keys()
+  ['snake']
+
+We can create a snake now::
+
+  >>> animal.create_animal('snake')
+  <Animal snake>
+
+Note that we can supply a different default value for the directive
+default when binding the directive to the grokker:
+
+  >>> class AnimalGrokker(AnimalGrokker):
+  ...   martian.directive(animal.name, default='generic animal')
+  ...
+  >>> class Generic(animal.Animal):
+  ...   pass
+  ...
+  >>> animal_grokker = AnimalGrokker()
+  >>> animal_grokker.grok('Generic', Generic)
+  True
+  >>> sorted(animal.all_animals.keys())
+  ['generic animal', 'snake']
+
+Moreover, we can also supply a default factory that may want to
+determine a dynamic default value based on the class that's being
+grokked.  For instance, let's say the default name of an animal should
+the class name converted to lowercase letters::
+
+  >>> def default_animal_name(class_, module, **data):
+  ...   return class_.__name__.lower()
+  ...
+  >>> class AnimalGrokker(AnimalGrokker):
+  ...   martian.directive(animal.name, get_default=default_animal_name)
+  ...
+  >>> class Mouse(animal.Animal):
+  ...   pass
+  ...
+  >>> animal_grokker = AnimalGrokker()
+  >>> animal_grokker.grok('Mouse', Mouse)
+  True
+  >>> sorted(animal.all_animals.keys())
+  ['generic animal', 'mouse', 'snake']
+
+Note that these default value factories will also get the data from
+all directives that are in front of them in the grokker's directive
+list.  For instance, consider the following directive:
+
+  >>> class zoologicalname(animal.name):
+  ...   pass
+  ...
+
+with the following default rule that takes the regular name as the
+default zoological name::
+
+  >>> def default_zoological_name(class_, module, name, **data):
+  ...   return name
+  ...
+  >>> class ZooAnimalGrokker(martian.ClassGrokker):
+  ...   martian.component(animal.Animal)
+  ...   martian.directive(animal.name, get_default=default_animal_name)
+  ...   martian.directive(zoologicalname, get_default=default_zoological_name)
+  ...
+  ...   def execute(self, class_, name, zoologicalname, **kw):
+  ...     print zoologicalname
+  ...     return True
+  ...
+  >>> class Hippopotamus(animal.Animal):
+  ...   pass
+  ...   # No need to use animal.name(), we'll take the class name as default.
+  ...   # The zoological name is the same as well.
+  ...
+
+  >>> zoo_animal_grokker = ZooAnimalGrokker()
+  >>> zoo_animal_grokker.grok('Hippopotamus', Hippopotamus)
+  hippopotamus
+  True
+
+If you pass a non-directive to ``martian.directive``, you get an error::
+
+  >>> class Test(martian.ClassGrokker):
+  ...    martian.directive('foo')
+  Traceback (most recent call last):
+  GrokImportError: The 'directive' directive can only be called with a directive.
+
+MethodGrokker
+-------------
+
+A special kind of class grokker is the ``MethodGrokker``.  It inspects
+the class at hand and calls ``execute`` for each *method* the class
+provides.
+
+Consider the following baseclass for circus animals:
+
+  >>> class CircusAnimal(animal.Animal):
+  ...   def begin_show(self):
+  ...     pass
+  ...   def end_show(self):
+  ...      pass
+
+Circus animals define lots of methods which we'll collect using this
+grokker:
+
+  >>> circus_animals = {}
+  >>> from martian import MethodGrokker
+  >>> class CircusAnimalGrokker(MethodGrokker):
+  ...   martian.component(CircusAnimal)
+  ...   def execute(self, class_, method, **kw):
+  ...     circus_animals.setdefault(class_.__name__, []).append(method.__name__)
+  ...     return True
+  ...
+
+Now consider the following circus animals:
+
+  >>> class Monkey(CircusAnimal):
+  ...   def climb(self):
+  ...     pass
+  ...   def _take_dump(self):
+  ...     pass
+  ...
+  >>> class Camel(CircusAnimal):
+  ...   def walk(self):
+  ...     pass
+  ...   def spit(self):
+  ...     pass
+
+  >>> circus_animal_grokker = CircusAnimalGrokker()
+  >>> circus_animal_grokker.grok('Monkey', Monkey)
+  True
+  >>> circus_animal_grokker.grok('Camel', Camel)
+  True
+
+Let's look at the results:
+
+  >>> for circus_animal, methods in sorted(circus_animals.items()):
+  ...     print "%s can %s." % (circus_animal, " and ".join(sorted(methods)))
+  ...
+  Camel can spit and walk.
+  Monkey can climb.
+
+As we see, private methods (those beginning with underscores) have
+been ignored.  Furthermore, methods inherited from the component
+baseclass (in this case ``CircusAnimal``) have also been ignored.
+
+If we wrote a class without any methods, we would encounter an error:
+
+  >>> class Snail(CircusAnimal):
+  ...   pass
+
+  >>> circus_animal_grokker.grok('Snail', Snail)
+  Traceback (most recent call last):
+    ...
+  GrokError: <class 'Snail'> does not define any public
+  methods. Please add methods to this class to enable its
+  registration.
+
+MultiClassGrokker
+-----------------
+
+We now want to be able to grok the following module and have the
+``Animal`` subclasses (but not the ``Chair`` class, which is not an
+animal) automatically become available::
+
+  >>> class animals(FakeModule):
+  ...   class Elephant(animal.Animal):
+  ...     animal.name('elephant')
+  ...   class Tiger(animal.Animal):
+  ...     animal.name('tiger')
+  ...   class Lion(animal.Animal):
+  ...     animal.name('lion')
+  ...   class Chair(object):
+  ...     animal.name('chair')
+  >>> animals = fake_import(animals)
+
+First we need to wrap our ``AnimalGrokker`` into a ``MultiClassGrokker``::
+
+ >>> from martian.core import MultiClassGrokker
+ >>> multi_grokker = MultiClassGrokker()
+ >>> multi_grokker.register(animal_grokker)
+
+Now let's wrap it into a ``ModuleGrokker`` and grok the module::
+
+  >>> grokker = martian.ModuleGrokker(grokker=multi_grokker)
+  >>> grokker.grok('animals', animals)
+  True
+
+The animals (but not anything else) should have become available::
+
+  >>> sorted(animal.all_animals.keys())
+  ['elephant', 'generic animal', 'lion', 'mouse', 'snake', 'tiger']
+
+We can create animals using their name now::
+
+  >>> animal.create_animal('elephant')
+  <Animal elephant>
+  >>> animal.create_animal('tiger')
+  <Animal tiger>
+
+MultiGrokker
+------------
+
+``MultiInstanceGrokker`` and ``MultiClassGrokker`` can grok instances
+and classes respectively, but a ``MultiInstanceGrokker`` won't work
+correctly if it runs into a class and vice versa. For that we use a
+``MultiGrokker``, which can deal with the full range of objects that
+can be grokked, and skips those it doesn't recognize.
+
+Let's fill a ``MultiGrokker`` with a bunch of grokkers::
+
+  >>> from martian import MultiGrokker
+  >>> multi = MultiGrokker()
+  >>> multi.register(filetype_grokker)
+  >>> multi.register(color_grokker)
+  >>> multi.register(sound_grokker)
+  >>> multi.register(animal_grokker)
+
+Let's try it with some individual objects::
+
+  >>> class Whale(animal.Animal):
+  ...    animal.name('whale')
+  >>> multi.grok('Whale', Whale)
+  True
+  >>> 'whale' in animal.all_animals
+  True
+
+This should have no effect, but not fail::
+
+  >>> my_whale = Whale()
+  >>> multi.grok('my_whale', my_whale)
+  False
+
+Grokked by the ColorGrokker::
+
+  >>> multi.grok('dark_grey', Color(50, 50, 50))
+  True
+  >>> 'dark_grey' in color.all_colors
+  True
+
+Grokked by the SoundGrokker::
+
+  >>> multi.grok('music', Sound('music'))
+  True
+  >>> 'music' in sound.all_sounds
+  True
+
+Not grokked::
+
+  >>> class RockMusic(Sound):
+  ...   pass
+  >>> multi.grok('RockMusic', RockMusic)
+  False
+
+Grokked by SoundGrokker::
+
+  >>> multi.grok('rocknroll', RockMusic('rock n roll'))
+  True
+  >>> 'rocknroll' in sound.all_sounds
+  True
+
+Not grokked::
+
+  >>> class Chair(object):
+  ...   pass
+  >>> multi.grok('Chair', Chair)
+  False
+
+Grokked by ``filetype_grokker``::
+
+  >>> def handle_py(filepath):
+  ...   return "Python file"
+  >>> multi.grok('handle_py', handle_py)
+  True
+  >>> '.py' in filehandler.extension_handlers
+  True
+
+Not grokked:
+
+  >>> def foo():
+  ...   pass
+  >>> multi.grok('foo', foo)
+  False
+
+Not grokked either::
+
+  >>> another = object()
+  >>> multi.grok('another', another)
+  False
+
+Let's make a module which has a mixture between classes and instances,
+some of which can be grokked::
+
+  >>> class mix(FakeModule):
+  ...   # grokked by AnimalGrokker
+  ...   class Whale(animal.Animal):
+  ...      animal.name('whale')
+  ...   # not grokked
+  ...   my_whale = Whale()
+  ...   # grokked by ColorGrokker
+  ...   dark_grey = Color(50, 50, 50)
+  ...   # grokked by SoundGrokker
+  ...   music = Sound('music')
+  ...   # not grokked
+  ...   class RockMusic(Sound):
+  ...      pass
+  ...   # grokked by SoundGrokker
+  ...   rocknroll = RockMusic('rock n roll')
+  ...   # grokked by AnimalGrokker
+  ...   class Dragon(animal.Animal):
+  ...     animal.name('dragon')
+  ...   # not grokked
+  ...   class Chair(object):
+  ...     pass
+  ...   # grokked by filetype_grokker
+  ...   def handle_py(filepath):
+  ...     return "Python file"
+  ...   # not grokked
+  ...   def foo():
+  ...     pass
+  ...   # grokked by AnimalGrokker
+  ...   class SpermWhale(Whale):
+  ...     animal.name('sperm whale')
+  ...   # not grokked
+  ...   another = object()
+  >>> mix = fake_import(mix)
+
+Let's construct a ``ModuleGrokker`` that can grok this module::
+
+  >>> mix_grokker = martian.ModuleGrokker(grokker=multi)
+
+Note that this is actually equivalent to calling ``ModuleGrokker``
+without arguments and then calling ``register`` for the individual
+``ClassGrokker`` and ``InstanceGrokker`` objects.
+
+Before we do the grokking, let's clean up our registration
+dictionaries::
+
+  >>> filehandler.extension_handlers = {}
+  >>> color.all_colors = {}
+  >>> sound.all_sounds = {}
+  >>> animal.all_animals = {}
+
+Now we grok::
+
+  >>> mix_grokker.grok('mix', mix)
+  True
+  >>> sorted(filehandler.extension_handlers.keys())
+  ['.py']
+  >>> sorted(color.all_colors.keys())
+  ['dark_grey']
+  >>> sorted(sound.all_sounds.keys())
+  ['music', 'rocknroll']
+  >>> sorted(animal.all_animals.keys())
+  ['dragon', 'sperm whale', 'whale']
+
+GlobalGrokker
+-------------
+
+Sometimes you want to let a grok action happen for each module. The
+grok action could for instance read the globals of a module, or even
+static files associated with the module by name. Let's create a module
+with some global value::
+
+  >>> class g(FakeModule):
+  ...   amount = 50
+  >>> g = fake_import(g)
+
+Now let's create a ``GlobalGrokker`` that reads ``amount`` and stores
+it in the ``read_amount`` dictionary::
+
+  >>> read_amount = {}
+  >>> from martian import GlobalGrokker
+  >>> class AmountGrokker(GlobalGrokker):
+  ...   def grok(self, name, module, **kw):
+  ...     read_amount[None] = module.amount
+  ...     return True
+
+Let's construct a ``ModuleGrokker`` with this ``GlobalGrokker`` registered::
+
+  >>> grokker = martian.ModuleGrokker()
+  >>> grokker.register(AmountGrokker())
+
+Now we grok and should pick up the right value::
+
+  >>> grokker.grok('g', g)
+  True
+  >>> read_amount[None]
+  50
+
+Old-style class support
+-----------------------
+
+So far we have only grokked either new-style classes or instances of
+new-style classes. It is also possible to grok old-style classes and
+their instances::
+
+  >>> class oldstyle(FakeModule):
+  ...   class Machine:
+  ...     pass
+  ...   all_machines = {}
+  ...   all_machine_instances = {}
+  >>> oldstyle = fake_import(oldstyle)
+
+Let's make a grokker for the old style class::
+
+  >>> class MachineGrokker(martian.ClassGrokker):
+  ...   martian.component(oldstyle.Machine)
+  ...   def grok(self, name, obj, **kw):
+  ...     oldstyle.all_machines[name] = obj
+  ...     return True
+
+And another grokker for old style instances::
+
+  >>> class MachineInstanceGrokker(martian.InstanceGrokker):
+  ...   martian.component(oldstyle.Machine)
+  ...   def grok(self, name, obj, **kw):
+  ...     oldstyle.all_machine_instances[name] = obj
+  ...     return True
+
+The multi grokker should succesfully grok the old-style ``Machine`` class
+and instances of it::
+
+  >>> multi = MultiGrokker()
+  >>> multi.register(MachineGrokker())
+  >>> multi.register(MachineInstanceGrokker())
+  >>> class Robot(oldstyle.Machine):
+  ...   pass
+  >>> multi.grok('Robot', Robot)
+  True
+  >>> oldstyle.all_machines.keys()
+  ['Robot']
+  >>> robot = Robot()
+  >>> multi.grok('robot', robot)
+  True
+  >>> oldstyle.all_machine_instances.keys()
+  ['robot']
+
+Grokking a package
+------------------
+
+A package consists of several sub modules. When grokking a package,
+all the files in the package will be grokked. Let's first create a simple
+grokker for the ``Animal`` class defined by the package::
+
+  >>> from martian.tests.testpackage import animal
+  >>> all_animals = {}
+  >>> class AnimalGrokker(martian.ClassGrokker):
+  ...   martian.component(animal.Animal)
+  ...   def grok(self, name, obj, **kw):
+  ...     all_animals[name] = obj
+  ...     return True
+
+The grokker will collect animals into the ``all_animals`` dictionary.
+
+Let's register this grokker for a ModuleGrokker::
+
+  >>> module_grokker = martian.ModuleGrokker()
+  >>> module_grokker.register(AnimalGrokker())
+
+Now let's grok the whole ``testpackage`` for animals::
+
+  >>> from martian import grok_dotted_name
+  >>> grok_dotted_name('martian.tests.testpackage', grokker=module_grokker)
+
+We should now get some animals::
+
+  >>> sorted(all_animals.keys())
+  ['Animal', 'Bear', 'Dragon', 'Lizard', 'Python', 'SpermWhale', 'Whale']
+
+Preparation and finalization
+----------------------------
+
+Before grokking a module, it may be that we need to do some
+preparation. This preparation can include setting up some parameters
+to pass along to the grokking process, for instance. We can pass
+a ``prepare`` function a the ModuleGrokker::
+
+  >>> class Number(object):
+  ...   def __init__(self, nr):
+  ...     self.nr = nr
+  >>> all_numbers = {}
+  >>> class NumberGrokker(martian.InstanceGrokker):
+  ...  martian.component(Number)
+  ...  def grok(self, name, obj, multiplier, **kw):
+  ...    all_numbers[obj.nr] = obj.nr * multiplier
+  ...    return True
+  >>> def prepare(name, module, kw):
+  ...   kw['multiplier'] = 3
+  >>> module_grokker = martian.ModuleGrokker(prepare=prepare)
+  >>> module_grokker.register(NumberGrokker())
+
+We have created a ``prepare`` function that does one thing: create a
+``multiplier`` parameter that is passed along the grokking
+process. The ``NumberGrokker`` makes use of this to prepare the
+``all_numbers`` dictionary values.
+
+Let's try this with a module::
+
+  >>> class numbers(FakeModule):
+  ...   one = Number(1)
+  ...   two = Number(2)
+  ...   four = Number(4)
+  >>> numbers = fake_import(numbers)
+  >>> module_grokker.grok('numbers', numbers)
+  True
+  >>> sorted(all_numbers.items())
+  [(1, 3), (2, 6), (4, 12)]
+
+You can also optionally register a finalization function, which will
+be run at the end of a module grok::
+
+  >>> def finalize(name, module, kw):
+  ...     all_numbers['finalized'] = True
+  >>> module_grokker = martian.ModuleGrokker(prepare=prepare, finalize=finalize)
+  >>> module_grokker.register(NumberGrokker())
+  >>> all_numbers = {}
+  >>> module_grokker.grok('numbers', numbers)
+  True
+  >>> 'finalized' in all_numbers
+  True
+
+Sanity checking
+---------------
+
+Grokkers must return ``True`` if grokking succeeded, or ``False`` if
+it didn't. If they return something else (typically ``None`` as the
+programmer forgot to), the system will raise an error::
+
+  >>> class BrokenGrokker(martian.InstanceGrokker):
+  ...  martian.component(Number)
+  ...  def grok(self, name, obj, **kw):
+  ...    pass
+
+  >>> module_grokker = martian.ModuleGrokker()
+  >>> module_grokker.register(BrokenGrokker())
+  >>> module_grokker.grok('numbers', numbers)
+  Traceback (most recent call last):
+    ...
+  GrokError: <BrokenGrokker object at ...> returns None instead of
+  True or False.
+
+Let's also try this with a GlobalGrokker::
+
+  >>> class MyGrokker(GlobalGrokker):
+  ...   def grok(self, name, module, **kw):
+  ...     return "Foo"
+  >>> module_grokker = martian.ModuleGrokker()
+  >>> module_grokker.register(MyGrokker())
+  >>> module_grokker.grok('numbers', numbers)
+  Traceback (most recent call last):
+    ...
+  GrokError: <MyGrokker object at ...> returns 'Foo' instead of True or False.
+
+Meta Grokkers
+-------------
+
+Meta grokkers are grokkers that grok grokkers. This mechanism can be
+used to extend Martian. Let's register a ``ClassMetaGrokker`` that
+looks for subclasses of ``ClassGrokker``::
+
+  >>> from martian.core import MetaGrokker
+  >>> class ClassMetaGrokker(MetaGrokker):
+  ...   martian.component(martian.ClassGrokker)
+  >>> multi_grokker = MultiGrokker()
+  >>> multi_grokker.register(ClassMetaGrokker(multi_grokker))
+
+``multi_grokker`` should now grok subclasses of ``ClassGrokker``, such
+as ``AnimalGrokker``::
+
+  >>> all_animals = {} # clean out animal registry
+  >>> multi_grokker.grok('AnimalGrokker', AnimalGrokker)
+  True
+
+Our multi_grokker should now also be able to grok animals::
+
+  >>> class Woodpecker(animal.Animal):
+  ...   pass
+  >>> multi_grokker.grok('Woodpecker', Woodpecker)
+  True
+
+A ``MetaMultiGrokker`` is a ``MultiGrokker`` that comes preconfigured with
+grokkers for ``ClassGrokker``, ``InstanceGrokker`` and ``GlobalGrokker``::
+
+  >>> from martian import MetaMultiGrokker
+  >>> multi_grokker = MetaMultiGrokker()
+
+It works for ``ClassGrokker``::
+
+  >>> all_animals = {}
+  >>> multi_grokker.grok('AnimalGrokker', AnimalGrokker)
+  True
+  >>> multi_grokker.grok('Woodpecker', Woodpecker)
+  True
+  >>> all_animals
+  {'Woodpecker': <class 'Woodpecker'>}
+
+and for ``InstanceGrokker``::
+
+  >>> color.all_colors = {}
+  >>> multi_grokker.grok('ColorGrokker', ColorGrokker)
+  True
+  >>> multi_grokker.grok('color', Color(255, 0, 0))
+  True
+  >>> color.all_colors
+  {'color': <Color 255 0 0>}
+
+and for ``GlobalGrokker``::
+
+  >>> read_amount = {}
+  >>> multi_grokker.grok('AmountGrokker', AmountGrokker)
+  True
+  >>> grokker.grok('g', g)
+  True
+  >>> read_amount[None]
+  50
+
+We can clear the meta multi grokker::
+
+  >>> multi_grokker.clear()
+
+It won't grok particular classes or instances anymore::
+
+  >>> multi_grokker.grok('Woodpecker', Woodpecker)
+  False
+  >>> multi_grokker.grok('color', Color(255, 0, 0))
+  False
+
+It can still grok grokkers::
+
+  >>> multi_grokker.grok('ColorGrokker', ColorGrokker)
+  True
+
+Executing meta grokkers only once
+---------------------------------
+
+In case of ``ClassGrokker`` and all other grokkers that are grokked
+by meta grokkers, we only want the grokking to occur once even if
+the same module (or package) is grokked twice::
+
+  >>> class TestOnce(object):
+  ...   pass
+  >>> executed = []
+  >>> class somemodule(FakeModule):
+  ...   class TestGrokker(martian.ClassGrokker):
+  ...     martian.component(TestOnce)
+  ...     def grok(self, name, obj, **kw):
+  ...        executed.append(name)
+  ...        return True
+  >>> somemodule = fake_import(somemodule)
+  >>> module_grokker = martian.ModuleGrokker(MetaMultiGrokker())
+
+Let's grok the module once::
+
+  >>> module_grokker.grok('somemodule', somemodule)
+  True
+
+Let's grok it twice::
+
+  >>> module_grokker.grok('somemodule', somemodule)
+  True
+
+Even though we have grokked it twice, it is still only registered once. We
+can show this by actually having it grok a ``TestOnce`` subclass::
+
+  >>> class anothermodule(FakeModule):
+  ...   class TestSub(TestOnce):
+  ...      pass
+  >>> anothermodule = fake_import(anothermodule)
+  >>> module_grokker.grok('anothermodule', anothermodule)
+  True
+  >>> executed
+  ['TestSub']
+
+This also works for instance grokkers::
+
+  >>> class TestInstanceOnce(object):
+  ...   pass
+  >>> executed = []
+  >>> class somemodule(FakeModule):
+  ...   class TestGrokker(martian.InstanceGrokker):
+  ...     martian.component(TestInstanceOnce)
+  ...     def grok(self, name, obj, **kw):
+  ...        executed.append(name)
+  ...        return True
+  >>> somemodule = fake_import(somemodule)
+  >>> module_grokker.clear()
+  >>> module_grokker.grok('somemodule', somemodule) # once
+  True
+  >>> module_grokker.grok('somemodule', somemodule) # twice
+  True
+  >>> class anothermodule(FakeModule):
+  ...   test = TestInstanceOnce()
+  >>> anothermodule = fake_import(anothermodule)
+  >>> module_grokker.grok('anothermodule', anothermodule)
+  True
+  >>> executed
+  ['test']
+
+It also works for global grokkers::
+
+  >>> executed = []
+  >>> class somemodule(FakeModule):
+  ...   class TestGrokker(GlobalGrokker):
+  ...     def grok(self, name, obj, **kw):
+  ...       executed.append(name)
+  ...       return True
+  >>> somemodule = fake_import(somemodule)
+  >>> module_grokker.clear()
+  >>> module_grokker.grok('somemodule', somemodule) # once
+  True
+  >>> module_grokker.grok('somemodule', somemodule) # twice
+  True
+
+The second grokking will already make ``somemodule`` grokked::
+
+  >>> executed
+  ['somemodule']
+
+Now let's grok another module::
+
+  >>> class anothermodule(FakeModule):
+  ...   pass
+  >>> anothermodule = fake_import(anothermodule)
+  >>> module_grokker.grok('anothermodule', anothermodule)
+  True
+  >>> executed
+  ['somemodule', 'anothermodule']
+
+Priority
+--------
+
+When grokking a module using a ``ModuleGrokker``, grokker execution
+can be determined by their priority. By default, grokkers have a
+priority of ``0``. Let's define two base classes, ``A`` and ``B``,
+which can be grokked::
+
+  >>> class A(object):
+  ...   pass
+
+  >>> class B(object):
+  ...   pass
+
+Let's define a special kind of class grokker that records the order in
+which names get grokked::
+
+  >>> order = []
+  >>> class OrderGrokker(martian.ClassGrokker):
+  ...   def grok(self, name, obj, **kw):
+  ...     order.append(name)
+  ...     return True
+
+Now we define two grokkers for subclasses of ``A`` and ``B``, where
+the ``BGrokker`` has a higher priority::
+
+  >>> class AGrokker(OrderGrokker):
+  ...   martian.component(A)
+  >>> class BGrokker(OrderGrokker):
+  ...   martian.component(B)
+  ...   martian.priority(10)
+
+Let's register these grokkers::
+
+  >>> multi_grokker = MetaMultiGrokker()
+  >>> multi_grokker.grok('AGrokker', AGrokker)
+  True
+  >>> multi_grokker.grok('BGrokker', BGrokker)
+  True
+
+Let's create a module containing ``A`` and ``B`` subclasses::
+
+  >>> class mymodule(FakeModule):
+  ...   class ASub(A):
+  ...     pass
+  ...   class BSub(B):
+  ...     pass
+  >>> mymodule = fake_import(mymodule)
+
+We'll grok it::
+
+  >>> module_grokker = martian.ModuleGrokker(multi_grokker)
+  >>> module_grokker.grok('mymodule', mymodule)
+  True
+
+Since the ``BGrokker`` has a higher priority, we expect the following
+order of grokking::
+
+  >>> order
+  ['BSub', 'ASub']
+
+This also works for GlobalGrokkers. We will define a GlobalGrokker
+that has a higher priority than the default, but lower than B::
+
+  >>> class MyGlobalGrokker(GlobalGrokker):
+  ...   martian.priority(5)
+  ...   def grok(self, name, obj, **kw):
+  ...     order.append(name)
+  ...     return True
+  >>> multi_grokker.grok('MyGlobalGrokker', MyGlobalGrokker)
+  True
+
+We will grok the module again::
+
+  >>> order = []
+  >>> module_grokker.grok('mymodule', mymodule)
+  True
+
+This time, the global grokker should appear after 'BSub' but before 'ASub'::
+
+  >>> order
+  ['BSub', 'mymodule', 'ASub']
+
+
+Module info
+-----------
+
+In addition to the ``name`` and ``object`` positional arguments,
+grokkers will get also get a ``module_info`` keyword argument.  It is
+an ``IModuleInfo`` object which can be used, for example, to query
+module annotations.  Consider the following grokker:
+
+  >>> from martian.error import GrokError
+  >>> class AnnotationsGrokker(GlobalGrokker):
+  ...   def grok(self, name, module, module_info, **kw):
+  ...       ann = module_info.getAnnotation('some.annotation', None)
+  ...       if ann is None:
+  ...           raise GrokError('Did not find annotation!', module)
+  ...       if ann != 'ME GROK SAY HI':
+  ...           raise GrokError('Wrong annotation!', module)
+  ...       return True
+
+Now let's provide a fake module:
+
+  >>> import new, sys
+  >>> annotations = new.module('annotations')
+  >>> annotations.__file__ = '/fake/module/annotations.py'
+  >>> sys.modules['annotations'] = annotations
+
+Clearly, it can't find the module-level variable yet:
+
+  >>> module_grokker = martian.ModuleGrokker()
+  >>> module_grokker.register(AnnotationsGrokker())
+  >>> import martian
+  >>> martian.grok_dotted_name('annotations', module_grokker)
+  Traceback (most recent call last):
+  ...
+  GrokError: Did not find annotation!
+
+Let's provide the annotation so that the grokker works as expected:
+
+  >>> annotations.__some_annotation__ = 'ME GROK SAY HI'
+  >>> martian.grok_dotted_name('annotations', module_grokker)
+
+Finally clean up:
+
+  >>> del sys.modules['annotations']

Copied: martian/trunk/src/martian/edgecase.txt (from rev 87194, martian/trunk/src/martian/core.txt)
===================================================================
--- martian/trunk/src/martian/edgecase.txt	                        (rev 0)
+++ martian/trunk/src/martian/edgecase.txt	2008-06-06 14:53:41 UTC (rev 87195)
@@ -0,0 +1,47 @@
+(Edge-case) tests of Martian core components
+============================================
+
+ModuleGrokker ignores values set by directives
+----------------------------------------------
+
+Consider the following module-level directive:
+
+  >>> import martian
+  >>> class store(martian.Directive):
+  ...     scope = martian.MODULE
+  ...     store = martian.ONCE
+  ...
+  >>> store.__module__ = 'somethingelse'  # just so that it isn't __builtin__
+
+Now let's look at a module that contains a simple function and a call
+to the directive defined above:
+
+  >>> class module_with_directive(FakeModule):
+  ...     fake_module = True
+  ...
+  ...     def some_function():
+  ...         return 11
+  ...
+  ...     store(some_function)
+  ...
+  >>> module_with_directive = fake_import(module_with_directive)
+
+Now imagine we have the following grokker for functions:
+
+  >>> import types
+  >>> class FunctionGrokker(martian.InstanceGrokker):
+  ...     martian.component(types.FunctionType)
+  ...     def grok(self, name, obj, **kw):
+  ...         print name, obj()
+  ...         return True
+  ...
+  >>> module_grokker = martian.ModuleGrokker()
+  >>> module_grokker.register(FunctionGrokker())
+
+and let it loose on the module, we see that it will only find functions
+set by regular variable assignment, not the ones stored by the
+directive:
+
+  >>> module_grokker.grok('module_with_directive', module_with_directive)
+  some_function 11
+  True

Modified: martian/trunk/src/martian/tests/test_all.py
===================================================================
--- martian/trunk/src/martian/tests/test_all.py	2008-06-06 14:42:13 UTC (rev 87194)
+++ martian/trunk/src/martian/tests/test_all.py	2008-06-06 14:53:41 UTC (rev 87195)
@@ -9,10 +9,6 @@
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTests([
-        doctest.DocFileSuite('tutorial.txt',
-                             package='martian',
-                             globs=globs,
-                             optionflags=optionflags),
         doctest.DocFileSuite('README.txt',
                              package='martian',
                              globs=globs,
@@ -28,6 +24,10 @@
                              package='martian',
                              globs=globs,
                              optionflags=optionflags),
+        doctest.DocFileSuite('edgecase.txt',
+                             package='martian',
+                             globs=globs,
+                             optionflags=optionflags),
         doctest.DocFileSuite('scan_for_classes.txt',
                              package='martian.tests',
                              optionflags=optionflags),

Deleted: martian/trunk/src/martian/tutorial.txt
===================================================================
--- martian/trunk/src/martian/tutorial.txt	2008-06-06 14:42:13 UTC (rev 87194)
+++ martian/trunk/src/martian/tutorial.txt	2008-06-06 14:53:41 UTC (rev 87195)
@@ -1,411 +0,0 @@
-Martian
-=======
-
-"There was so much to grok, so little to grok from." -- Stranger in a
-Strange Land, by Robert A. Heinlein
-
-Martian provides infrastructure for declarative configuration of
-Python code. Martian is especially useful for the construction of
-frameworks that need to provide a flexible plugin
-infrastructure. Martian doesn't actually provide any infrastructure
-for plugin registries and such. Many frameworks have their own, and if
-you need a generic one, you might want to consider
-``zope.component``. Martian just allows you to make the registration
-of such plugins less verbose. 
-
-You can see Martian as doing something that you can also solve with
-metaclasses, with the following advantages:
-
-* the developer of the framework doesn't have to write a lot of ad-hoc
-  metaclasses anymore; instead we offer an infrastructure to make life
-  easier.
-
-* configuration doesn't need to happen at import time, but can happen at
-  program startup time. This also makes configuration more tractable for
-  a developer.
-
-* we don't bother the developer that *uses* the framework with the
-  surprising behavior that metaclasses sometimes bring. The classes
-  the user has to deal with are normal classes.
-
-Why is this package named ``martian``? In the novel "Stranger in a
-Strange Land", the verb *grok* is introduced:
-
-  Grok means to understand so thoroughly that the observer becomes a
-  part of the observed -- to merge, blend, intermarry, lose identity
-  in group experience.
-
-In the context of this package, "grokking" stands for the process of
-deducing declarative configuration actions from Python code. In the
-novel, grokking is originally a concept that comes from the planet
-Mars. Martians *grok*. Since this package helps you grok code, it's
-called Martian.
-
-The ``martian`` package is a spin-off from the `Grok project`_, in the
-context of which this codebase was first developed. While Grok uses
-it, the code is completely independent of Grok.
-
-.. _`Grok project`: http://grok.zope.org
-
-Motivation
-----------
-
-"Deducing declarative configuration actions from Python code" - that
-sounds very abstract. What does it actually mean? What is
-configuration?  What is declarative configuration? In order to explain
-this, we'll first take a look at configuration.
-
-Larger frameworks often offer a lot of points where you can modify
-their behavior: ways to combine its own components with components you
-provide yourself to build a larger application. A framework offers
-points where it can be *configured* with plugin code. When you plug
-some code into a plugin point, it results in the updating of some
-registry somewhere with the new plugin. When the framework uses a
-plugin, it will first look it up in the registry. The action of
-registering some component into a registry can be called
-*configuration*.
-
-Let's look at an example framework that offers a plugin point. We
-introduce a very simple framework for plugging in different template
-languages, where each template language uses its own extension. You
-can then supply the framework with the template body and the template
-extension and some data, and render the template.
-
-Let's look at the framework::
-
-  >>> import string
-  >>> class templating(FakeModule):
-  ...
-  ...   class InterpolationTemplate(object):
-  ...      "Use %(foo)s for dictionary interpolation."
-  ...      def __init__(self, text):
-  ...          self.text = text
-  ...      def render(self, **kw):
-  ...          return self.text % kw
-  ...
-  ...   class TemplateStringTemplate(object):
-  ...      "PEP 292 string substitutions."
-  ...      def __init__(self, text):
-  ...          self.template = string.Template(text)
-  ...      def render(self, **kw):
-  ...          return self.template.substitute(**kw)
-  ...
-  ...   # the registry, we plug in the two templating systems right away
-  ...   extension_handlers = { '.txt': InterpolationTemplate, 
-  ...                          '.tmpl': TemplateStringTemplate }
-  ...
-  ...   def render(data, extension, **kw):
-  ...      """Render the template at filepath with arguments.
-  ...  
-  ...      data - the data in the file
-  ...      extension - the extension of the file
-  ...      keyword arguments - variables to interpolate
-  ...
-  ...      In a real framework you could pass in the file path instead of
-  ...      data and extension, but we don't want to open files in our
-  ...      example.
-  ...
-  ...      Returns the rendered template
-  ...      """
-  ...      template = extension_handlers[extension](data)
-  ...      return template.render(**kw)
-
-Since normally we cannot create modules in a doctest, we have emulated
-the ``templating`` Python module using the ``FakeModule``
-class. Whenever you see ``FakeModule`` subclasses, imagine you're
-looking at a module definition in a ``.py`` file. Now that we have
-defined a module ``templating``, we also need to be able to import
-it. To do so we can use a a fake import statement that lets us do
-this::
-
-  >>> templating = fake_import(templating)
-
-Now let's try the ``render`` function for the registered template
-types, to demonstrate that our framework works::
-
-  >>> templating.render('Hello %(name)s!', '.txt', name="world")
-  'Hello world!'
-  >>> templating.render('Hello ${name}!', '.tmpl', name="universe")
-  'Hello universe!'
-
-File extensions that we do not recognize cause a ``KeyError`` to be
-raised::
-
-  >>> templating.render('Hello', '.silly', name="test")
-  Traceback (most recent call last):
-  ...
-  KeyError: '.silly'
-
-We now want to plug into this filehandler framework and provide a
-handler for ``.silly`` files. Since we are writing a plugin, we cannot
-change the ``templating`` module directly. Let's write an extension
-module instead::
-
-  >>> class sillytemplating(FakeModule):
-  ...   class SillyTemplate(object):
-  ...      "Replace {key} with dictionary values."
-  ...      def __init__(self, text):
-  ...          self.text = text
-  ...      def render(self, **kw):
-  ...          text = self.text
-  ...          for key, value in kw.items():
-  ...              text = text.replace('{%s}' % key, value)
-  ...          return text
-  ...
-  ...   templating.extension_handlers['.silly'] = SillyTemplate
-  >>> sillytemplating = fake_import(sillytemplating)
-
-In the extension module, we manipulate the ``extension_handlers``
-dictionary of the ``templating`` module (in normal code we'd need to
-import it first), and plug in our own function. ``.silly`` handling
-works now::
-
-  >>> templating.render('Hello {name}!', '.silly', name="galaxy")
-  'Hello galaxy!'
-
-Above we plug into our ``extension_handler`` registry using Python
-code. Using separate code to manually hook components into registries
-can get rather cumbersome - each time you write a plugin, you also
-need to remember you need to register it. 
-
-Doing template registration in Python code also poses a maintenance
-risk. It is tempting to start doing fancy things in Python code such
-as conditional configuration, making the configuration state of a
-program hard to understand. Another problem is that doing
-configuration at import time can also lead to unwanted side effects
-during import, as well as ordering problems, where you want to import
-something that really needs configuration state in another module that
-is imported later. Finally, it can also make code harder to test, as
-configuration is loaded always when you import the module, even if in
-your test perhaps you don't want it to be.
-
-Martian provides a framework that allows configuration to be expressed
-in declarative Python code. Martian is based on the realization that
-what to configure where can often be deduced from the structure of
-Python code itself, especially when it can be annotated with
-additional declarations. The idea is to make it so easy to write and
-register a plugin so that even extensive configuration does not overly
-burden the developer. 
-
-Configuration actions are executed during a separate phase ("grok
-time"), not at import time, which makes it easier to reason about and
-easier to test.
-
-Configuration the Martian Way
------------------------------
-
-Let's now transform the above ``templating`` module and the
-``sillytemplating`` module to use Martian. First we must recognize
-that every template language is configured to work for a particular
-extension. With Martian, we annotate the classes themselves with this
-configuration information. Annotations happen using *directives*,
-which look like function calls in the class body.
-
-Let's create an ``extension`` directive that can take a single string
-as an argument, the file extension to register the template class
-for::
-
-  >>> import martian
-  >>> class extension(martian.Directive):
-  ...   scope = martian.CLASS
-  ...   store = martian.ONCE
-  ...   default = None
-
-We also need a way to easily recognize all template classes. The normal
-pattern for this in Martian is to use a base class, so let's define a
-``Template`` base class::
-
-  >>> class Template(object):
-  ...   pass
-
-We now have enough infrastructure to allow us to change the code to use
-Martian style base class and annotations::
-
-  >>> class templating(FakeModule):
-  ...
-  ...   class InterpolationTemplate(Template):
-  ...      "Use %(foo)s for dictionary interpolation."
-  ...      extension('.txt')
-  ...      def __init__(self, text):
-  ...          self.text = text
-  ...      def render(self, **kw):
-  ...          return self.text % kw
-  ...
-  ...   class TemplateStringTemplate(Template):
-  ...      "PEP 292 string substitutions."
-  ...      extension('.tmpl')
-  ...      def __init__(self, text):
-  ...          self.template = string.Template(text)
-  ...      def render(self, **kw):
-  ...          return self.template.substitute(**kw)
-  ...
-  ...   # the registry, empty to start with
-  ...   extension_handlers = {}
-  ...
-  ...   def render(data, extension, **kw):
-  ...      # this hasn't changed
-  ...      template = extension_handlers[extension](data)
-  ...      return template.render(**kw)
-  >>> templating = fake_import(templating)
-
-As you can see, there have been very few changes:
-
-* we made the template classes inherit from ``Template``.
-
-* we use the ``extension`` directive in the template classes.
-
-* we stopped pre-filling the ``extension_handlers`` dictionary.
-
-So how do we fill the ``extension_handlers`` dictionary with the right
-template languages? Now we can use Martian. We define a *grokker* for
-``Template`` that registers the template classes in the
-``extension_handlers`` registry::
-
-  >>> class meta(FakeModule):   
-  ...   class TemplateGrokker(martian.ClassGrokker):
-  ...     martian.component(Template)
-  ...     martian.directive(extension)
-  ...     def execute(self, class_, extension, **kw):
-  ...       templating.extension_handlers[extension] = class_
-  ...       return True
-  >>> meta = fake_import(meta)
-
-What does this do? A ``ClassGrokker`` has its ``execute`` method
-called for subclasses of what's indicated by the ``martian.component``
-directive. You can also declare what directives a ``ClassGrokker``
-expects on this component by using ``martian.directive()`` (the
-``directive`` directive!) one or more times. 
-
-The ``execute`` method takes the class to be grokked as the first
-argument, and the values of the directives used will be passed in as
-additional parameters into the ``execute`` method. The framework can
-also pass along an arbitrary number of extra keyword arguments during
-the grokking process, so we need to declare ``**kw`` to make sure we
-can handle these.
-
-All our grokkers will be collected in a special Martian-specific
-registry::
-
-  >>> reg = martian.GrokkerRegistry()
-
-We will need to make sure the system is aware of the
-``TemplateGrokker`` defined in the ``meta`` module first, so let's
-register it first. We can do this by simply grokking the ``meta``
-module::
-
-  >>> reg.grok('meta', meta)
-  True
-
-Because ``TemplateGrokker`` is now registered, our registry now knows
-how to grok ``Template`` subclasses. Let's grok the ``templating``
-module::
-
-  >>> reg.grok('templating', templating)
-  True
-
-Let's try the ``render`` function of templating again, to demonstrate
-we have successfully grokked the template classes::
-
-  >>> templating.render('Hello %(name)s!', '.txt', name="world")
-  'Hello world!'
-  >>> templating.render('Hello ${name}!', '.tmpl', name="universe")
-  'Hello universe!'
-
-``.silly`` hasn't been registered yet::
-
-  >>> templating.render('Hello', '.silly', name="test")
-  Traceback (most recent call last):
-  ...
-  KeyError: '.silly'
-
-Let's now register ``.silly`` from an extension module::
-
-  >>> class sillytemplating(FakeModule):
-  ...   class SillyTemplate(Template):
-  ...      "Replace {key} with dictionary values."
-  ...      extension('.silly')
-  ...      def __init__(self, text):
-  ...          self.text = text
-  ...      def render(self, **kw):
-  ...          text = self.text
-  ...          for key, value in kw.items():
-  ...              text = text.replace('{%s}' % key, value)
-  ...          return text
-  >>> sillytemplating = fake_import(sillytemplating)
-
-As you can see, the developer that uses the framework has no need
-anymore to know about ``templating.extension_handlers``. Instead we can
-simply grok the module to have ``SillyTemplate`` be register appropriately::
-
-  >>> reg.grok('sillytemplating', sillytemplating)
-  True
-
-We can now use the ``.silly`` templating engine too::
-
-  >>> templating.render('Hello {name}!', '.silly', name="galaxy")
-  'Hello galaxy!'
-
-Admittedly it is hard to demonstrate Martian well with a small example
-like this. In the end we have actually written more code than in the
-basic framework, after all. But even in this small example, the
-``templating`` and ``sillytemplating`` module have become more
-declarative in nature. The developer that uses the framework will not
-need to know anymore about things like
-``templating.extension_handlers`` or an API to register things
-there. Instead the developer can registering a new template system
-anywhere, as long as he subclasses from ``Template``, and as long as
-his code is grokked by the system.
-
-Finally note how Martian was used to define the ``TemplateGrokker`` as
-well. In this way Martian can use itself to extend itself.
-
-Grokking instances
-------------------
-
-Above we've seen how you can grok classes. Martian also supplies a way
-to grok instances. This is less common in typical frameworks, and has
-the drawback that no class-level directives can be used, but can still
-be useful.
-
-Let's imagine a case where we have a zoo framework with an ``Animal``
-class, and we want to track instances of it::
-
-  >>> class Animal(object):
-  ...   def __init__(self, name):
-  ...     self.name = name
-  >>> class zoo(FakeModule):
-  ...   horse = Animal('horse')
-  ...   chicken = Animal('chicken')
-  ...   elephant = Animal('elephant')
-  ...   lion = Animal('lion')
-  ...   animals = {}
-  >>> zoo = fake_import(zoo)
- 
-We define an ``InstanceGrokker`` subclass to grok ``Animal`` instances::
-
-  >>> class meta(FakeModule):   
-  ...   class AnimalGrokker(martian.InstanceGrokker):
-  ...     martian.component(Animal)
-  ...     def execute(self, instance, **kw):
-  ...       zoo.animals[instance.name] = instance
-  ...       return True
-  >>> meta = fake_import(meta)
-
-Let's create a new registry with the ``AnimalGrokker`` in it::
-   
-  >>> reg = martian.GrokkerRegistry()
-  >>> reg.grok('meta', meta)
-  True
-
-We can now grok the ``zoo`` module::
-
-  >>> reg.grok('zoo', zoo)
-  True
-
-The animals will now be in the ``animals`` dictionary::
-
-  >>> sorted(zoo.animals.items())
-  [('chicken', <Animal object at ...>), 
-   ('elephant', <Animal object at ...>), 
-   ('horse', <Animal object at ...>), 
-   ('lion', <Animal object at ...>)]



More information about the Checkins mailing list