Merge pull request #266 from xobs/add-moduledoc-autodoc

Add ModuleDoc and AutoDoc
This commit is contained in:
enjoy-digital 2019-09-24 10:09:22 +02:00 committed by GitHub
commit 836d5b88c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 3 deletions

View File

@ -8,9 +8,9 @@ from migen import *
from litex.soc.interconnect.csr import *
from litex.soc.interconnect.csr_eventmanager import *
from litex.soc.integration.doc import ModuleDoc
class Timer(Module, AutoCSR):
class Timer(Module, AutoCSR, ModuleDoc):
"""Timer
Provides a generic Timer core.
@ -21,7 +21,7 @@ class Timer(Module, AutoCSR):
- Periodic: (Re-)Loads itself when value reaches 0.
`en` register allows the user to enable/disable the Timer. When the Timer is enabled, it is
automatically loaded with the value of `load' register.
automatically loaded with the value of `load` register.
When the Timer reaches 0, it is automatically reloaded with value of `reload` register.

View File

@ -0,0 +1,150 @@
# This file is Copyright (c) 2019 Sean Cross <sean@xobs.io>
from migen.fhdl.module import DUID
from migen.util.misc import xdir
from litex.soc.interconnect.csr_eventmanager import EventManager
import textwrap
import inspect
class ModuleDoc(DUID):
"""Module Documentation Support
ModuleDoc enables you to add documentation to your Module. This documentation is in addition to
any CSR-level documentation you may add to your module, if applicable.
There are two ways to use :obj:`ModuleDoc`:
1. Inherit :obj:`ModuleDoc` as part of your class. The docstring of your class will become the
first section of your class' module documentation
2. Add a :obj:`ModuleDoc` object to your class and inherit from :obj:`AutoDoc`.
If you inherit from :obj:`ModuleDoc`, then there is no need to call ``__init__()``
Synopsis
--------
::
class SomeClass(Module, ModuleDoc, AutoDoc):
\"\"\"Some Special Hardware Module
This is a hardware module that implements something really cool.
\"\"\"
def __init__(self):
self.other_section = ModuleDoc(title="Protocol Details", body="This section details more
information about the protocol")
"""
def __init__(self, body=None, title=None, file=None, format="rst"):
"""Construct a :obj:`ModuleDoc` object for use with :obj:`AutoDoc`
Arguments
---------
body (:obj:`str`): Main body of the document. If ``title`` is omitted, then the
title is taken as the first line of ``body``.
title (:obj:`str` Optional): Title of this particular section.
file (:obj:`str` Optional): It is possible to load the documentation from an external
file instead of specifying it inline. This allows for the use of an external text
editor. If a ``file`` is specified, then it will override the ``body`` argument.
format (:obj:`str` Optional): The text format. Python prefers reStructured Text, so this
defaults to `rst`. If specifying a `file`, then the suffix will be used instead of
`format`. If you specify a format other than `rst`, you may need to install a converter.
"""
import os
DUID.__init__(self)
self._title = title
self._format = format
if file == None and body == None and self.__doc__ is None:
raise ValueError("Must specify `file` or `body` when constructing a ModuleDoc()")
if file is not None:
if not os.path.isabs(file):
relative_path = inspect.stack()[1][1]
file = os.path.dirname(relative_path) + os.path.sep + file
(_, self._format) = os.path.splitext(file)
self._format = self._format[1:] # Strip off "." from extension
# If it's a reStructured Text file, read the whole thing in.
if self._format == "rst":
with open(file, "r") as f:
self.__doc__ = f.read()
# Otherwise, we'll simply make a link to it and let sphinx take care of it
else:
self._path = file
elif body is not None:
self.__doc__ = body
def title(self):
# This object might not have _title as an attribute, because
# the ModuleDoc constructor may not have been called. If this
# is the case, manipulate the __doc__ string directly.
if hasattr(self, "_title") and self._title is not None:
return self._title
_lines = self.__doc__.splitlines()
return textwrap.dedent(_lines[0])
def body(self):
if hasattr(self, "_title") and self._title is not None:
return self.__doc__
_lines = self.__doc__.splitlines()
_lines.pop(0)
return textwrap.dedent("\n".join(_lines))
def format(self):
if hasattr(self, "_format") and self._format is not None:
return self._format
return "rst"
def path(self):
if hasattr(self, "_path"):
return self._path
return None
def documentationprefix(prefix, documents, done):
for doc in documents:
if doc.duid not in done:
# doc.name = prefix + doc.name
done.add(doc.duid)
def _make_gatherer(method, cls, prefix_cb):
def gatherer(self):
try:
exclude = self.autodoc_exclude
except AttributeError:
exclude = {}
try:
prefixed = self.__prefixed
except AttributeError:
prefixed = self.__prefixed = set()
r = []
for k, v in xdir(self, True):
if k not in exclude:
if isinstance(v, cls):
r.append(v)
elif hasattr(v, method) and callable(getattr(v, method)):
items = getattr(v, method)()
prefix_cb(k + "_", items, prefixed)
r += items
return sorted(r, key=lambda x: x.duid)
return gatherer
class AutoDoc:
"""MixIn to provide documentation support.
A module can inherit from the ``AutoDoc`` class, which provides ``get_module_documentation``.
This will iterate through all objects looking for ones that inherit from ModuleDoc.
If the module has child objects that implement ``get_module_documentation``,
they will be called by the``AutoCSR`` methods and their documentation added to the lists returned,
with the child objects' names as prefixes.
"""
get_module_documentation = _make_gatherer("get_module_documentation", ModuleDoc, documentationprefix)