Should be used as a decorator to *attach* import statments to function
    definitions. These imports are added to the global (i.e. module-level of 
    the decorated function) namespace.
        
    Two forms of import statements are supported (in the following examples
    ``foo``, ``bar``, ``oof, and ``rab`` are modules not classes or functions)::

        import foo, bar              # -> @imports(['foo', 'bar'])
        import foo.oof as oof            
        import bar.rab as rab        # -> @imports(['foo.oof', 'bar.rab'])
        
    It provides support for alternatives::
    
        try:
            import foo
        except ImportError:
            import bar
            
    which is expressed as::
    
        @imports(['foo,bar'])
        
    or alternatively::
    
        try:
            import foo.oof as oof
        except ImportError:
            import bar.rab as oof
            
    becomes::
    
        @imports(['foo.oof,bar.rab'])
        
    
    This import is available in the body of the function as ``oof`` All needed
    imports should be attached for every function (even if two function are in
    the same module and have the same ``globals``)

    Arguments:

        - modules (``list``) A list of modules in the following forms 
          ``['foo', 'bar', ..., 'baz']`` or  
          ``['foo.oof', 'bar.rab', ..., 'baz.zab']``
        - forgive (``bool``) [default: ``False``] If ``True`` will not raise 
          `ImportError``

def imports(modules, forgive=False):
    """
    Should be used as a decorator to *attach* import statments to function
    definitions. These imports are added to the global (i.e. module-level of 
    the decorated function) namespace.
        
    Two forms of import statements are supported (in the following examples
    ``foo``, ``bar``, ``oof, and ``rab`` are modules not classes or functions)::

        import foo, bar              # -> @imports(['foo', 'bar'])
        import foo.oof as oof            
        import bar.rab as rab        # -> @imports(['foo.oof', 'bar.rab'])
        
    It provides support for alternatives::
    
        try:
            import foo
        except ImportError:
            import bar
            
    which is expressed as::
    
        @imports(['foo,bar'])
        
    or alternatively::
    
        try:
            import foo.oof as oof
        except ImportError:
            import bar.rab as oof
            
    becomes::
    
        @imports(['foo.oof,bar.rab'])
        
    
    This import is available in the body of the function as ``oof`` All needed
    imports should be attached for every function (even if two function are in
    the same module and have the same ``globals``)

    Arguments:

        - modules (``list``) A list of modules in the following forms 
          ``['foo', 'bar', ..., 'baz']`` or  
          ``['foo.oof', 'bar.rab', ..., 'baz.zab']``
        - forgive (``bool``) [default: ``False``] If ``True`` will not raise 
          `ImportError``
    """
    def wrap(f):
        if modules:
            # attach import to function
            setattr(f, 'imports', modules)
            for alternatives in modules:
                # alternatives are comma seperated
                alternatives = alternatives.split(',')
                # we import the part of the import X.Y.Z -> Z
                mod_name = alternatives[0].split('.')[-1]
                for mod in alternatives:
                    mod = mod.strip().split('.')

                    try:
                        if len(mod) == 1:
                            module = __import__(mod[0])
                        else:
                            module = getattr(__import__('.'.join(mod[:-1]), \
                                            fromlist=[mod[-1]]), mod[-1])
                        f.func_globals[mod_name] = module
                        break # import only one
                    except ImportError:
                        pass
                else:
                    if forgive: # no break -> no import
                        warnings.warn('Failed to import %s' % alternatives)
                    else:
                        raise ImportError('Failed to import %s' % alternatives)
        return f
    return wrap