Source code for openmdao.util.doctools

"""
A utility to extract Traits information from the code and get it into the
Sphinx documentation. 

.. note:: No traits docs will be generated unless the class containing the traits has a doc string!
"""

from sys import maxint, float_info

from enthought.traits.api import HasTraits, MetaHasTraits, Any, Python, Event, \
                                 Instance
from enthought.traits.trait_types import _InstanceArgs
from inspect import getmro, ismodule, getmembers, ismethod, isfunction, isclass

from openmdao.main.datatypes.api import Slot

excludes = (Any, Python, Event, type)

def _print_funct(funct, args, kw):
    arglst = []
    if args:
        for arg in args:
            if isinstance(arg, basestring):
                arglst.append("'%s'" % arg)
            else:
                arglst.append("%s" % arg)
    argstr = ', '.join(arglst)
    kwlst = []
    if kw:
        for k,v in kw.items():
            if isinstance(v,basestring):
                kwlst.append("%s='%s'" % (k,v))
            else:
                kwlst.append("%s=%s" % (k,v))
    kwstr = ', '.join(kwlst)
    if kwstr and len(argstr)>0:
        kwstr = ', '+kwstr
    return "%s(%s%s)" % (funct.__name__, argstr, kwstr)

def _get_instance_default(trait):
    if trait.factory:
        return _print_funct(trait.factory, trait.args, trait.kw)
    elif isinstance(trait.default_value, _InstanceArgs):
        return _print_funct(trait.default_value.args[0],
                            trait.default_value.args[1:],
                            trait.default_value.kw)
    return trait.default_value

[docs]def get_traits_info(app, what, name, obj, options, lines): """ Gets traits info. """ #Get the API information for OpenMDAO if name.endswith(".api") and ismodule(obj): #get functions fns = getmembers(obj, isfunction) for n,v in fns: filename = v.__module__ + ".py" lines.append(":ref:`%s<%s>`" %(n,filename)) lines.append('\n') #get classes cls = getmembers(obj, isclass) for n1, v1 in cls: module = v1.__module__ if module=="enthought.traits.trait_types": filename2 = ("http://code.enthought.com/projects/files/ETS32_API/enthought.traits.trait_types.%s.html" %n1) lines.append("`%s <%s>`_" %(n1, filename2)) else: filename2 = module + ".py" lines.append(":ref:`%s<%s>`" %(n1,filename2)) lines.append('\n') if not isinstance(obj, (MetaHasTraits, HasTraits)): return #gets a list of the class heirarchy. classes = getmro(obj) #gets a dict of all traits in this class, including those from base classes this_class_traits = obj.class_traits() #gets a dict of all traits in this class' base class base_class_traits = {} for cls in classes[1:]: if hasattr(cls, 'class_traits'): base_class_traits.update(cls.class_traits()) #The things we want to keep will be those that are #unique to the current class, and those that appear in the #base but are overridden by the current class. So keep each #item in this class' list that doesn't appear in the #base classes' lists. keepers = {} for trt, trt_val in this_class_traits.items(): if not base_class_traits.has_key(trt): keepers[trt] = trt_val else: #the names are the same, so check the objects #to see if they are the same. if they aren't, # there's an override in this class; keep it! if not trt_val == base_class_traits[trt]: keepers[trt] = trt_val keepers_in={} keepers_out={} keepers_instance={} keepers_undefined={} #Now we need to SORT the traits by input/output type. for t,val in keepers.items(): #As long as it's not an excluded type, add it. if not isinstance(val.trait_type, excludes): if val.trait_type._metadata.has_key("iotype"): if val.trait_type._metadata["iotype"] =="in": keepers_in[t]=val elif val.trait_type._metadata["iotype"] == "out": keepers_out[t]=val elif type(val.trait_type).__name__ in ["Instance","Slot"]: keepers_instance[t]=val else: keepers_undefined[t]=val dicts = (keepers_instance, keepers_in, keepers_out, keepers_undefined) dontdo_meta=set(['iotype', 'units', 'low', 'high', 'type', 'desc', 'instance_handler', 'parent', 'array']) for dic in dicts: sortedDict = _sortedDictVals(dic) for t, val in sortedDict: lines.append('') #Now just need to spit out the traits in the proper format into the documentation if val.is_trait_type(Instance) or val.is_trait_type(Slot): lines.extend(["*%s* (%s) **%s**" %(type(val.trait_type).__name__, val.trait_type.klass.__name__, t)]) else: lines.extend(["*%s* **%s**" %(type(val.trait_type).__name__, t)]) if val.desc is not None: lines.extend([" %s" %val.desc]) lines.append('') if val.is_trait_type(Instance) or val.is_trait_type(Slot): lines.extend([" * default: %s" % _get_instance_default(val.trait_type)]) else: lines.extend([" * default: '%s'" %(val.trait_type).default_value]) if val.iotype is not None: lines.extend([" * iotype: '%s'" %val.iotype]) if val.units is not None: lines.extend([" * units: '%s'" %val.units]) if val.low is not None: if val.low == (-1 * maxint): continue elif val.low == (-float_info.max): continue else: lines.extend([' * low: %s' %val.low]) if val.high is not None: if val.high is maxint: continue elif val.high is float_info.max: continue else: lines.extend([' * high: %s' %val.high]) #now to put in the metadata added by users, or not specially handled. metadata = val.trait_type._metadata.items() for m, v in metadata: if m not in dontdo_meta: if isinstance(v, basestring): v = "'%s'" % v lines.extend([' * %s: %s' %(m, v)]) lines.append('')
[docs]def setup(app): """ Connect the doctools to the process-docstring hook. """ app.connect('autodoc-process-docstring', get_traits_info)
def _sortedDictVals(unsorted_dict): """ Sort a dictionary into alphabetical order by keys. """ items = unsorted_dict.items() items.sort() return items if __name__ == '__main__': # pragma no cover from openmdao.lib.components.api import ExternalCode lines = [] get_traits_info(None, 'class', 'foo', ExternalCode, None, lines) for line in lines: print line
OpenMDAO Home