mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
150 lines
5.7 KiB
Python
150 lines
5.7 KiB
Python
# 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 is None and body is 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)
|