Friday, November 11, 2011

The DocMeta Module

Shared-code location: dl.dropbox.com/u/1917253/site-packages/DocMeta.py

Back on Monday, I promised I'd share my UnitTestUtilities and DocMeta modules. Hopefully you've already seen UnitTestUtilities, since I posted that on Wednesday. Today's foray is into DocMeta.

I wrote DocMeta quite some time back, in order to provide a simple, annotation-based (e.g., decorator-based) way of adding additional/more specific documentation-information to functions and methods. You've already seen it in use in UnitTestUtilities, at least from the code side of things, and if you've tried to take a look at the pydoc output for it, it's probably barfed at you, saying that it couldn't find DocMeta.

What DocMeta does, is provide decorators that add information to the docstring of a function or method. Consider the HasTestFor function from UnitTestUtilities:
@ToDo( 'Unit-test the function' )
@ToDo( 'Document exceptions raised' )
@ToDo( 'Value-check arguments' )
@ToDo( 'Type-check arguments' )
@DocumentArgument( 'argument', 'testCase', None, '(unittest.TestCase, required) The TestCase instance to check for the named test-method in.' )
@DocumentArgument( 'argument', 'itemName', None, '(Class, required) The name of the property or method to look for a test-method for ("test[itemName]").' )
@DocumentReturn( types.BooleanType )
def HasTestFor( testCase, itemName ):
    """Returns true if the testCase provided has a test-method named 'test[itemName]'."""

Without the decorators, the pydoc documentation for the function would look something like so:
HasTestFor(testCase, itemName)
Returns true if the testCase provided has a test-method named 'test[itemName]'.

With the decorators, the this is what it looks like:
HasTestFor(testCase, itemName)
Returns true if the testCase provided has a test-method named 'test[itemName]'. 
  
################### 
## TO-DOs        ## 
################### 
 - Type-check arguments  
 - Value-check arguments  
 - Document exceptions raised  
 - Unit-test the function  
  
################### 
## Returns       ## 
################### 
bool 
  
################### 
## Arguments     ## 
################### 
 + testCase ............ (unittest.TestCase, required) The TestCase instance to  
                         check for the named test-method in.  
 + itemName ............ (Class, required) The name of the property or method  
                         to look for a test-method for ("test[itemName]").

The basic pattern that these decorators follow is to add the supplied meta-data to the item being decorated as protected attributes of the item (_argumentDocumentation, _configDocumentation, etc.), with helper functions to assure that the meta-data structure exists, and to re-generate the item's docstring as needed.

As I contine working on this module, I'm planning to add in classes and functionality to generate documentation as LaTeX/PDF, HTML and/or XML output from the additional meta-data, but for now, I'm just going to share/show the code for the decorators...
# DocMeta.py
"""Provides common documentation-metadata decorators, that facilitate in-code 
documentation of functions, classes, their methods, etc."""

__author__  = 'Brian D. Allbee'
__licence__ = 'http://creativecommons.org/licenses/by-sa/3.0/'
__version__ = '0.2'
__copyright__ = "Copyright 2011, Brian D. Allbee"
__credits__ = ["Brian D. Allbee"]
__maintainer__ = "Brian D. Allbee"
__email__ = "brian.allbee@gmail.com"
__status__ = "Development"

#####################################
# Build an "__all__" list to        #
# support                           #
# "from Namespace.blah import *"    #
# syntax                            #
#####################################
__all__ = []

#####################################
# Required imports                  #
#####################################

import inspect

#####################################
# Constants defined in the module   #
#####################################

SelfDocumentationString = 'The object-instance that the method is called against.'

__all__ += [ 'SelfDocumentationString' ]

#####################################
# Functions defined in the module   #
#####################################

def HangWrap( baseString, lineLength=80, indent=30 ):
    """Helper/formatting function that wraps a string to the specified line-length, with the specified hanging indent."""
    remainder = baseString.split( ' ' )
    result = ''
    line = ''
    while remainder:
        theWord = remainder[0]
        remainder = remainder[1:]
        if not theWord in [ '\n', '\r' ]:
            if len( line ) + len( theWord ) + 1 > lineLength:
                result += line + '\n'
                line = ' ' * ( indent + 1)
            line += theWord + ' '
    result += line + '\n'
    return result

__all__ += [ 'HangWrap' ]

def InitializeDocMetadata( decoratedItem ):
    """Helper function that initializes all of the decorated item's documentation-metadata structures."""
    if not hasattr( decoratedItem, '_configDocumentation' ):
        setattr( decoratedItem, '_configDocumentation', {} )
    if not hasattr( decoratedItem, '_argumentDocumentation' ):
        setattr( decoratedItem, '_argumentDocumentation', { 'args':{}, 'arglist':{}, 'kwargs':{} } )
    if not hasattr( decoratedItem, '_baseDocumentation' ):
        setattr( decoratedItem, '_baseDocumentation', decoratedItem.__doc__ )
        if not getattr( decoratedItem, '_baseDocumentation' ):
            setattr( decoratedItem, '_baseDocumentation', '' )
    if not hasattr( decoratedItem, '_exceptionDocumentation' ):
        setattr( decoratedItem, '_exceptionDocumentation', {} )
    if not hasattr( decoratedItem, '_returnsDocumentation' ):
        setattr( decoratedItem, '_returnsDocumentation', [] )
    if not hasattr( decoratedItem, '_todoDocumentation' ):
        setattr( decoratedItem, '_todoDocumentation', [] )

__all__ += [ 'InitializeDocMetadata' ]

def RebuildDocumentation( decoratedItem ):
    """Helper function that rebuilds documentation metadata and stuffs it into the item's __doc__."""

    # Base documentation, if any, first...
    tmpDocs = getattr( decoratedItem, '_baseDocumentation' )
    if not tmpDocs:
        tmpDocs = 'No base documentation provided.'
    tmpDocs += '\n'

    # TODO documentation next
    toDoDocs = getattr( decoratedItem, '_todoDocumentation' )
    if len( toDoDocs ) != 0:
        tmpDocs += """
###################
## TO-DOs        ##
###################
"""
        for theToDo in toDoDocs:
            tmpDocs += HangWrap( ' - %s' % ( theToDo ), 80, 6 )

    # Returns documentation next
    returnDocs = getattr( decoratedItem, '_returnsDocumentation' )
    tag = ''
    if len( returnDocs ) > 1:
        tmpDocs += """
###################
## Returns       ##
###################
Any of %s
""" % ( returnDocs )
    elif len( returnDocs ) == 1:
        tmpDocs += """
###################
## Returns       ##
###################
%s
""" % ( returnDocs[ 0 ] )

    # Arguments next
    argDocs = getattr( decoratedItem, '_argumentDocumentation' )
    argSpecs = inspect.getargspec( decoratedItem )
    if argSpecs[0] or argSpecs[1] or argSpecs[2]:
        tmpDocs += """
###################
## Arguments     ##
###################
"""
    if argSpecs[0]:
        for theArg in argSpecs[0]:
            if theArg in argDocs['args'].keys():
                tmpDocs += HangWrap( ( ' + %s ........................' % ( theArg ) )[:24] + ' ' + argDocs['args'][theArg], 80, 24 )
    if argSpecs[1]:
        tmpDocs += HangWrap( ( ' + %s ........................' % ( argSpecs[1] ) )[:24] + ' ' + argDocs['arglist'], 80, 24 )
    if argSpecs[2] and argDocs['kwargs']:
        tmpDocs += ( ' + %s ........................' % ( argSpecs[2] ) )[:24] + ' Keyword-argument list. The following values are known/documented:\n'
        theKeys = argDocs['kwargs'].keys()
        theKeys.sort()
        for theKey in theKeys:
            tmpDocs += HangWrap( ( '   - %s ........................' % ( theKey ) )[:24] + ' ' + str( argDocs['kwargs'][theKey] ), 80, 24 )

    exceptionDocs = getattr( decoratedItem, '_exceptionDocumentation' )
    if exceptionDocs:
        tmpDocs += """
###################
## Exceptions    ##
###################
"""
    exceptions = exceptionDocs.keys()
    exceptions.sort()
    for theException in exceptions:
        tmpDocs += 'Raises %s when:\n' % ( theException )
        for details in exceptionDocs[ theException ]:
            tmpDocs += HangWrap( ' - %s;' % ( details ), 80, 6 )

    configDocs = getattr( decoratedItem, '_configDocumentation' )
    if configDocs:
        tmpDocs += """
###################
## Configuration ##
###################

The relevant configuration-file section(s) and values(s) are:
"""
        sectionKeys = configDocs.keys()
        sectionKeys.sort()
        for theSection in sectionKeys:
            tmpDocs += '[%s]\n' % ( theSection )
            items = configDocs[ theSection ]
            itemKeys = items.keys()
            itemKeys.sort()
            for theItem in itemKeys:
                tmpDocs += HangWrap( ( '%s .....................................' % ( theItem ) )[:29] + ' %s' % ( items[ theItem ] ), 80, 29 )

    setattr( decoratedItem, '__doc__', tmpDocs )

__all__ += [ 'RebuildDocumentation' ]

def DocumentArgument( argumentType, argumentName, keywordName, argumentDocumentation ):
    """Decorator function that documents a function- or method-argument."""
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        argSpecs = inspect.getargspec( decoratedItem )
        if argSpecs[0]:
            for theName in argSpecs[0]:
                if theName != 'self':
                    if not theName in decoratedItem._argumentDocumentation[ 'args' ].keys():
                        decoratedItem._argumentDocumentation[ 'args' ][ theName ] = 'Not documented.'
                else:
                    decoratedItem._argumentDocumentation[ 'args' ][ theName ] = SelfDocumentationString
        if argSpecs[1]:
            if not decoratedItem._argumentDocumentation[ 'arglist' ]:
                decoratedItem._argumentDocumentation[ 'arglist' ] = argSpecs[1]
        if argSpecs[2]:
            if not decoratedItem._argumentDocumentation[ 'kwargs' ]:
                decoratedItem._argumentDocumentation[ 'kwargs' ] = {}

        if argumentType == "argument":
            argDocs = getattr( decoratedItem, '_argumentDocumentation' )[ 'args' ]
            argDocs[ argumentName ] = argumentDocumentation

        elif argumentType == "arglist":
            pass

        elif argumentType == "keyword":
            argDocs = getattr( decoratedItem, '_argumentDocumentation' )[ 'kwargs' ]
            if keywordName in argDocs.keys():
                raise AttributeError( '"%s" has already been documented as a keyword value for the "%s" list of %s.' % ( keywordName, argumentName, decoratedItem.__name__ ) )
            argDocs[ keywordName ] = argumentDocumentation

        else:
            raise AttributeError( '"%s" is not a recognized argumentType ("argument", "arglist", or "keyword" are allowed).' % ( argumentType ) )

        # Re-create the item's docstring with the argument information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ 'DocumentArgument' ]

def DocumentConfiguration( configSection, configName, configDescription ):
    """Decorator function that documents the configuration-file section(s) relevant to 
instances of the object. Intended for use with ConfigParser instances, but may 
be valid for other configuration mechanisms."""
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        configDocs = getattr( decoratedItem, '_configDocumentation' )
        if not configSection in configDocs.keys():
            configDocs[ configSection ] = {}
        configDocs[ configSection ][ configName ] = configDescription

        # Re-create the item's docstring with the exception information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ 'DocumentConfiguration' ]

def DocumentException( exceptionType, exceptionCase ):
    """Decorator function that documents an exception that can be raised by a function or method."""
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        exceptionDocs = getattr( decoratedItem, '_exceptionDocumentation' )
        if not exceptionType.__name__ in exceptionDocs.keys():
            exceptionDocs[ exceptionType.__name__ ] = []
        exceptionDocs[ exceptionType.__name__ ].append( exceptionCase )

        # Re-create the item's docstring with the exception information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ 'DocumentException' ]

def DocumentReturn( *returnTypes ):
    """Decorator function that documents the return type(s) of a function or method."""
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        returnDocs = getattr( decoratedItem, '_returnsDocumentation' )
        for theType in returnTypes:
            if theType != None:
                if not theType.__name__ in returnDocs:
                    returnDocs.append( theType.__name__ )
            else:
                if not 'None' in returnDocs:
                    returnDocs.append( 'None' )
        returnDocs.sort()
        # Re-create the item's docstring with the return information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ 'DocumentReturn' ]

def ToDo( todo ):
    """Decorator function that documents "To-do" items."""
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        toDoDocs = getattr( decoratedItem, '_todoDocumentation' )
        toDoDocs.append( todo )
        # Re-create the item's docstring with the todo information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ 'ToDo' ]

#####################################
# Decorate above functions          #
#####################################

# DocumentArgument
DocumentArgument( 'argument', 'argumentType', None, '(\'argument\', \'arglist\' or \'keyword\') The argument-type for the argument.' )( DocumentArgument )
DocumentArgument( 'argument', 'argumentName', None, '(String, required) The name of the argument of the function or method being documented.' )( DocumentArgument )
DocumentArgument( 'argument', 'keywordName', None, '(String, required for keyword arguments, ignored for other types) The name of a keyword argument that is recognized by the function or method.' )( DocumentArgument )
DocumentArgument( 'argument', 'argumentDocumentation', None, '(String) The documentation for the argument.' )( DocumentArgument )
ToDo( 'Type-check arguments.' )( DocumentArgument )
ToDo( 'Value-check arguments(?).' )( DocumentArgument )
ToDo( 'Document exceptions' )( DocumentArgument )

# DocumentConfiguration
DocumentArgument( 'argument', 'configSection', None, '(String, required) The name of the configuration-section that the configName argument will be found in.' )( DocumentConfiguration )
DocumentArgument( 'argument', 'configName', None, '(String, required) The name of the configuration-value.' )( DocumentConfiguration )
DocumentArgument( 'argument', 'configDescription', None, '(String) The documentation for the configuration name/value pair.' )( DocumentConfiguration )
ToDo( 'Type-check arguments.' )( DocumentConfiguration )
ToDo( 'Value-check arguments(?).' )( DocumentConfiguration )
ToDo( 'Document exceptions' )( DocumentConfiguration )

# DocumentException
DocumentArgument( 'argument', 'exceptionType', None, '(Exception, required) The exception-type expected under the case in exceptionCase.' )( DocumentException )
DocumentArgument( 'argument', 'exceptionCase', None, '(String) The case that will raise the specified exception. Documentation will be built as a list of cases for each exception, in a format similar to "Raises [exceptionType] when:" followed by all exception-cases for that type.' )( DocumentException )
ToDo( 'Type-check arguments.' )( DocumentException )
ToDo( 'Value-check arguments(?).' )( DocumentException )
ToDo( 'Document exceptions' )( DocumentException )

# DocumentReturn
DocumentArgument( 'arglist', 'returnTypes', None, 'The types returned by the function or method.' )( DocumentReturn )
ToDo( 'Type-check arguments.' )( DocumentReturn )
ToDo( 'Value-check arguments(?).' )( DocumentReturn )
ToDo( 'Document exceptions' )( DocumentReturn )

# HangWrap
DocumentArgument( 'argument', 'baseString', None, '(String, required) The base string to re-format.' )( HangWrap )
DocumentArgument( 'argument', 'lineLength', None, '(Integer, optional, defaults to 80) The maximum length of a line in the re-formatted string.' )( HangWrap )
DocumentArgument( 'argument', 'indent', None, '(Integer, optional, defaults to 30) The number of spaces to indent second and subsequent lines in the re-formatted string' )( HangWrap )
ToDo( 'Type-check arguments.' )( HangWrap )
ToDo( 'Value-check arguments(?).' )( HangWrap )
ToDo( 'Document exceptions' )( HangWrap )

# InitializeDocMetadata
DocumentArgument( 'argument', 'decoratedItem', None, 'The item to initialize documentation-meta-data structures on.' )( InitializeDocMetadata )
ToDo( 'Type-check arguments.' )( InitializeDocMetadata )
ToDo( 'Value-check arguments(?).' )( InitializeDocMetadata )
ToDo( 'Document exceptions' )( InitializeDocMetadata )

# RebuildDocumentation
DocumentArgument( 'argument', 'decoratedItem', None, 'The item to rebuild __doc__ on, using current documentation-meta-data structure.' )( RebuildDocumentation )
ToDo( 'Type-check arguments.' )( RebuildDocumentation )
ToDo( 'Value-check arguments(?).' )( RebuildDocumentation )
ToDo( 'Document exceptions' )( RebuildDocumentation )

# ToDo
DocumentArgument( 'argument', 'todo', None, '(String, required) The "To-do" item to attach.' )( ToDo )
ToDo( 'Type-check arguments.' )( ToDo )
ToDo( 'Value-check arguments(?).' )( ToDo )
ToDo( 'Document exceptions' )( ToDo )

#####################################
# Interfaces defined in the module  #
#####################################

#####################################
# Abstract Classes defined in the   #
# module                            #
#####################################

#####################################
# Classes defined in the module     #
#####################################

#####################################
# Package sub-modules and -packages #
#####################################

#####################################
# Unit-test the module on main      #
#####################################

if __name__ == '__main__':
    import inspect, os, sys, unittest
    from UnitTestUtilities import *

    testSuite = unittest.TestSuite()
    testResults = unittest.TestResult()

    ##################################
    # Unit-test Constants            #
    ##################################

    class testConstants( unittest.TestCase ):
        """Unit-tests the constants defined in the module."""
        def testConstants( self ):
            self.assertEquals( SelfDocumentationString, 'The object-instance that the method is called against.', 'SelfDocumentationString should be a constant, meaningful string.' )
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testConstants ) )

    ##################################
    # Unit-test Functions            #
    ##################################

    class testDocumentArgument( unittest.TestCase ):
        """Unit-tests the DocumentArgument function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentArgument ) )

    class testDocumentConfiguration( unittest.TestCase ):
        """Unit-tests the DocumentConfiguration function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentConfiguration ) )

    class testDocumentException( unittest.TestCase ):
        """Unit-tests the DocumentException function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentException ) )

    class testDocumentReturn( unittest.TestCase ):
        """Unit-tests the DocumentReturn function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentReturn ) )

    class testHangWrap( unittest.TestCase ):
        """Unit-tests the HangWrap function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testHangWrap ) )

    class testInitializeDocMetadata( unittest.TestCase ):
        """Unit-tests the InitializeDocMetadata function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testInitializeDocMetadata ) )

    class testRebuildDocumentation( unittest.TestCase ):
        """Unit-tests the RebuildDocumentation function defined in the module."""
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testRebuildDocumentation ) )

    ##################################
    # Unit-test Interfaces           #
    ##################################

    ##################################
    # Unit-test Abstract Classes     #
    ##################################

    ##################################
    # Unit-test Classes              #
    ##################################

    # Run unit-tests and install
    print '#'*79
    print 'Running unit-tests'
    testSuite.run( testResults )
    print '#'*79
    if testResults.errors == [] and testResults.failures == []:
        print 'All unit-tests passed! Ready for build/copy!'
        import shutil
        sitePackagesDirs = []
        for path in sys.path:
            if path.split( os.sep )[-1].lower() == 'site-packages':
                sitePackagesDirs.append( path )
        for path in sitePackagesDirs:
            destinationFile = path + os.sep + __file__
            try:
                shutil.copyfile( './%s' % __file__, destinationFile )
                print 'Copied %s to %s' % ( __file__, path )
            except:
                print 'Could not copy file %s to location %s' % ( __file__, path )
    else:
        print 'Unit-tests failed (see items below)'
        print '#'*79
        if testResults.errors:
            print 'Errors'
            print '#' + '-'*77 + '#'
            for error in testResults.errors:
                print error[1]
                print '#' + '-'*77 + '#'
            print
        if testResults.failures:
            if testResults.errors:
                print '#'*79
            print 'Failures'
            print '#' + '-'*77 + '#'
            for failure in testResults.failures:
                print failure[1]
                print '#' + '-'*77 + '#'
            print
    print '#'*79

As with UnitTestUtilities, I didn't want to hold up sharing this code for the sake of writing a lot of unit-tests. In this case, I'm not quite sure what my testing strategy is going to be. But as I write out those tests, the updated version of the module will be shared at the location noted above, so keep watching if you have the interest...
Commentary:
Line(s)
20, 34, 56, etc.
This is my usual __all__ set-up
26
The decorators use functionality from the inspect module, so we import it here
32
Rather than have a magic string embedded in the argument-decorator, though that would probably be OK, I opted to make the SelfDocumentationString constant, and use it elsewhere. The name could be changed, it's a long name to type over and over again, but since most of it's uses can be copied and pasted quickly and easily, I didn't think it was a big deal.
40-54
HangWrap is just a string-formatting function, that provides the nice, clean dot-leader-with-hanging-indent shown in the doc-string example for HasTestFor, above. It's a convenience function, nothing more, and I may not bother with writing any significant unit-tests for it later (though I should, for the sake of completion).
58-73
InitializeDocMetadata is a critical function - it assures that the documentation-meta-data structure needed for most (all?) of the decorators here is created, but not disturbed if it already exists. I'm not completely happy with the function, but I couldn't come up with a better way to handle the need, and I expect that it will perform well (though since it's not called outside of the context of a decorator that runs once at "compile-time," even that's not much of a concern). The items it ensures exist are:
_configDocumentation
A dictionary that will eventually contain config-section keys to dictionaries of config-names/values.
_argumentDocumentation
A dictionary of argument-, argument-list- and keyword-dictionaries
_baseDocumentation
The original __doc__ of the decorated item.
_exceptionDocumentation
A dictionary of Exception keys to condition-string lists
_returnDocumentation
A list of types
_todoDocumentation
A list of to-do strings
77-172
This is the function that generates the final, meta-data-inclusive docstring that we see when browsing decorated items with pydoc. It uses HangWrap to format long lines into hanging-indent lines, but is pretty straightforward, I believe. Just long, since there's a lot of stuff to look for and format, potentially.
174-217
DocumentArgument is (probably) the most commonly used decorator of this whole set, and likely deserves some in-depth commentary:
Line(s)
177
Makes sure that the meta-data structure is available on the decorated item.
179
Gets ahold of the list of arguments that were defined by the code using inspect functionality.
180-186
Checks for the existence of the argument-name, documents it with the SelfDocumentationString value if it's self, or with "Not documented" by default, after checking to see that it hasn't already been documented.
187-189
Stuffs an inspected argument-list's name into the meta-data structure.
190-191
Stuffs an inspected keyword-arument-list's name into the meta-data structure.
194-196
Sets the documentation-string for the specified argument.
198-199
Skips any further processing for an argument-list, since there can be only one, and it's done by the time we reach this section of the code.
201-205
Adds the specified keyword-name and documentation-value to the keyword-argument's meta-data (a dictionary of known keywords, and their documentation, if you recall).
211
Rebuilds the decorated item's __doc__.

The patterns of various branches of DocumentArgument are echoed in DocumentConfiguration, DocumentException, DocumentReturn and ToDo, albeit with different and shallower meta-data-structure targets - hopefully, their functionality is straightforward.

Lines 298 through 356 explicitly decorate all of the decorator functions after they have all been defined, in order to provide the (hopefully) expected documentation for them. The structure/syntax probably looks a bit weird if you aren't used to it, but it does the same thing as the typical @DecoratorFunction(args) call right before the item being decorated...

The final doc-strings of each of these items demonstrate that:

DocumentArgument(argumentType, argumentName, keywordName, argumentDocumentation)
    Decorator function that documents a function- or method-argument.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + argumentType ........ ('argument', 'arglist' or 'keyword') The argument-type 
                             for the argument. 
     + argumentName ........ (String, required) The name of the argument of the 
                             function or method being documented. 
     + keywordName ......... (String, required for keyword arguments, ignored for 
                             other types) The name of a keyword argument that is 
                             recognized by the function or method. 
     + argumentDocumentation (String) The documentation for the argument.

DocumentConfiguration(configSection, configName, configDescription)
    Decorator function that documents the configuration-file section(s) relevant to 
    instances of the object. Intended for use with ConfigParser instances, but may 
    be valid for other configuration mechanisms.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + configSection ....... (String, required) The name of the 
                             configuration-section that the configName argument 
                             will be found in. 
     + configName .......... (String, required) The name of the 
                             configuration-value. 
     + configDescription ... (String) The documentation for the configuration 
                             name/value pair.

DocumentException(exceptionType, exceptionCase)
    Decorator function that documents an exception that can be raised by a function or method.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + exceptionType ....... (Exception, required) The exception-type expected 
                             under the case in exceptionCase. 
     + exceptionCase ....... (String) The case that will raise the specified 
                             exception. Documentation will be built as a list of 
                             cases for each exception, in a format similar to 
                             "Raises [exceptionType] when:" followed by all 
                             exception-cases for that type.

DocumentReturn(*returnTypes)
    Decorator function that documents the return type(s) of a function or method.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + returnTypes ......... returnTypes

HangWrap(baseString, lineLength=80, indent=30)
    Helper/formatting function that wraps a string to the specified line-length, with the specified hanging indent.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + baseString .......... (String, required) The base string to re-format. 
     + lineLength .......... (Integer, optional, defaults to 80) The maximum length 
                             of a line in the re-formatted string. 
     + indent .............. (Integer, optional, defaults to 30) The number of 
                             spaces to indent second and subsequent lines in the 
                             re-formatted string

InitializeDocMetadata(decoratedItem)
    Helper function that initializes all of the decorated item's documentation-metadata structures.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + decoratedItem ....... The item to initialize documentation-meta-data 
                             structures on.

RebuildDocumentation(decoratedItem)
    Helper function that rebuilds documentation metadata and stuffs it into the item's __doc__.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + decoratedItem ....... The item to rebuild __doc__ on, using current 
                             documentation-meta-data structure.

ToDo(todo)
    Decorator function that documents "To-do" items.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + todo ................ (String, required) The "To-do" item to attach.

No comments:

Post a Comment