Source code for traits.util.trait_documenter
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
"""
A Trait Documenter
(Subclassed from the autodoc ClassLevelDocumenter)
"""
from importlib import import_module
import inspect
import io
import token
import tokenize
import traceback
from sphinx.ext.autodoc import ClassLevelDocumenter
from sphinx.util import logging
from traits.has_traits import MetaHasTraits
from traits.trait_type import TraitType
from traits.traits import generic_trait
logger = logging.getLogger(__name__)
def _is_class_trait(name, cls):
""" Check if the name is in the list of class defined traits of ``cls``.
"""
return (
isinstance(cls, MetaHasTraits)
and name in cls.__class_traits__
and cls.__class_traits__[name] is not generic_trait
)
[docs]class TraitDocumenter(ClassLevelDocumenter):
""" Specialized Documenter subclass for trait attributes.
The class defines a new documenter that recovers the trait definition
signature of module level and class level traits.
To use the documenter, append the module path in the extension
attribute of the `conf.py`.
"""
# ClassLevelDocumenter interface #####################################
objtype = "traitattribute"
directivetype = "attribute"
member_order = 60
# must be higher than other attribute documenters
priority = 12
[docs] @classmethod
def can_document_member(cls, member, membername, isattr, parent):
""" Check that the documented member is a trait instance.
"""
check = (
isattr
and issubclass(type(member), TraitType)
or _is_class_trait(membername, parent.object)
)
return check
[docs] def document_members(self, all_members=False):
""" Trait attributes have no members """
pass
[docs] def import_object(self):
""" Get the Trait object.
Notes
-----
Code adapted from autodoc.Documenter.import_object.
"""
try:
current = self.module = import_module(self.modname)
for part in self.objpath[:-1]:
current = self.get_attr(current, part)
name = self.objpath[-1]
self.object_name = name
self.object = None
self.parent = current
return True
# this used to only catch SyntaxError, ImportError and
# AttributeError, but importing modules with side effects can raise
# all kinds of errors.
except Exception as err:
if self.env.app and not self.env.app.quiet:
self.env.app.info(traceback.format_exc().rstrip())
msg = (
"autodoc can't import/find {0} {r1}, it reported error: "
'"{2}", please check your spelling and sys.path'
)
self.directive.warn(
msg.format(self.objtype, str(self.fullname), err)
)
self.env.note_reread()
return False
[docs]def trait_definition(*, cls, trait_name):
""" Retrieve the portion of the source defining a Trait attribute.
For example, given a class::
class MyModel(HasStrictTraits)
foo = List(Int, [1, 2, 3])
``trait_definition(cls=MyModel, trait_name="foo")`` returns
``"List(Int, [1, 2, 3])"``.
Parameters
----------
cls : MetaHasTraits
Class being documented.
trait_name : str
Name of the trait being documented.
Returns
-------
str
The portion of the source containing the trait definition. For
example, for a class trait defined as ``"my_trait = Float(3.5)"``,
the returned string will contain ``"Float(3.5)"``.
Raises
------
ValueError
If *trait_name* doesn't appear as a class-level variable in the
source.
"""
# Get the class source and tokenize it.
source = inspect.getsource(cls)
string_io = io.StringIO(source)
tokens = tokenize.generate_tokens(string_io.readline)
# find the trait definition start
trait_found = False
name_found = False
while not trait_found:
item = next(tokens, None)
if item is None:
break
if name_found and item[:2] == (token.OP, "="):
trait_found = True
continue
if item[:2] == (token.NAME, trait_name):
name_found = True
if not trait_found:
raise ValueError(
"No trait definition for {!r} found in {!r}".format(
trait_name, cls)
)
# Retrieve the trait definition.
definition_tokens = _get_definition_tokens(tokens)
definition = tokenize.untokenize(definition_tokens).strip()
return definition
def _get_definition_tokens(tokens):
""" Given the tokens, extracts the definition tokens.
Parameters
----------
tokens : iterator
An iterator producing tokens.
Returns
-------
A list of tokens for the definition.
"""
# Retrieve the trait definition.
definition_tokens = []
first_line = None
for type, name, start, stop, line_text in tokens:
if first_line is None:
first_line = start[0]
if type == token.NEWLINE:
break
item = (
type,
name,
(start[0] - first_line + 1, start[1]),
(stop[0] - first_line + 1, stop[1]),
line_text,
)
definition_tokens.append(item)
return definition_tokens
[docs]def setup(app):
""" Add the TraitDocumenter in the current sphinx autodoc instance. """
app.add_autodocumenter(TraitDocumenter)