"""
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 enthought.traits.api import HasTraits, MetaHasTraits, Any, Python, Event
from enthought.traits.trait_base import not_none
from inspect import getmro, ismodule, getmembers, ismethod, isclass
import inspect
from sys import maxint, float_info
from enthought.traits.api import Instance
excludes = (Any, Python, Event, type)
[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, inspect.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, inspect.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) or isinstance(obj, 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 = classes[1].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__ == "Instance"):
keepers_instance[t]=val
else:
keepers_undefined[t]=val
dicts = (keepers_instance, keepers_in, keepers_out, keepers_undefined)
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)):
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('')
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.
dontdo=('iotype', 'units', 'low', 'high', 'type', 'desc', 'instance_handler', 'parent', 'array')
metadata = val.trait_type._metadata.items()
for m, v in metadata:
if m not in dontdo:
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