Source code
Revision control
Copy as Markdown
Other Tools
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
import importlib
from pathlib import Path
from docutils import nodes
from docutils.parsers.rst import Directive
from mots.config import FileConfig
from mots.directory import Directory
from mots.export import export_to_format
from sphinx.util.docstrings import prepare_docstring
from sphinx.util.docutils import ReferenceRole
def function_reference(f, attr, args, doc):
lines = []
lines.extend(
[
f,
"-" * len(f),
"",
]
)
docstring = prepare_docstring(doc)
lines.extend(
[
docstring[0],
"",
]
)
arg_types = []
for t in args:
if isinstance(t, list):
inner_types = [t2.__name__ for t2 in t]
arg_types.append(" | ".join(inner_types))
continue
arg_types.append(t.__name__)
arg_s = "(%s)" % ", ".join(arg_types)
lines.extend(
[
":Arguments: %s" % arg_s,
"",
]
)
lines.extend(docstring[1:])
lines.append("")
return lines
def variable_reference(v, st_type, in_type, doc):
lines = [
v,
"-" * len(v),
"",
]
docstring = prepare_docstring(doc)
lines.extend(
[
docstring[0],
"",
]
)
lines.extend(
[
":Storage Type: ``%s``" % st_type.__name__,
":Input Type: ``%s``" % in_type.__name__,
"",
]
)
lines.extend(docstring[1:])
lines.append("")
return lines
def special_reference(v, func, typ, doc):
lines = [
v,
"-" * len(v),
"",
]
docstring = prepare_docstring(doc)
lines.extend(
[
docstring[0],
"",
":Type: ``%s``" % typ.__name__,
"",
]
)
lines.extend(docstring[1:])
lines.append("")
return lines
def format_module(m):
lines = []
lines.extend(
[
".. note::",
" moz.build files' implementation includes a ``Path`` class.",
]
)
path_docstring_minus_summary = prepare_docstring(m.Path.__doc__)[2:]
lines.extend([" " + line for line in path_docstring_minus_summary])
for subcontext, cls in sorted(m.SUBCONTEXTS.items()):
lines.extend(
[
".. _mozbuild_subcontext_%s:" % subcontext,
"",
"Sub-Context: %s" % subcontext,
"=============" + "=" * len(subcontext),
"",
]
)
lines.extend(prepare_docstring(cls.__doc__))
if lines[-1]:
lines.append("")
for k, v in sorted(cls.VARIABLES.items()):
lines.extend(variable_reference(k, *v))
lines.extend(
[
"Variables",
"=========",
"",
]
)
for v in sorted(m.VARIABLES):
lines.extend(variable_reference(v, *m.VARIABLES[v]))
lines.extend(
[
"Functions",
"=========",
"",
]
)
for func in sorted(m.FUNCTIONS):
lines.extend(function_reference(func, *m.FUNCTIONS[func]))
lines.extend(
[
"Special Variables",
"=================",
"",
]
)
for v in sorted(m.SPECIAL_VARIABLES):
lines.extend(special_reference(v, *m.SPECIAL_VARIABLES[v]))
return lines
def find_mots_config_path(app):
"""Find and return mots config path if it exists."""
base_path = Path(app.srcdir).parent
config_path = base_path / "mots.yaml"
if config_path.exists():
return config_path
def export_mots(config_path):
"""Load mots configuration and export it to file."""
# Load from disk and initialize configuration and directory.
config = FileConfig(config_path)
config.load()
directory = Directory(config)
directory.load()
# Fetch file format (i.e., "rst") and export path.
frmt = config.config["export"]["format"]
path = config_path.parent / config.config["export"]["path"]
# Generate output.
output = export_to_format(directory, frmt)
# Create export directory if it does not exist.
path.parent.mkdir(parents=True, exist_ok=True)
# Write changes to disk.
with path.open("w", encoding="utf-8") as f:
f.write(output)
class MozbuildSymbols(Directive):
"""Directive to insert mozbuild sandbox symbol information."""
required_arguments = 1
def run(self):
module = importlib.import_module(self.arguments[0])
fname = module.__file__
if fname.endswith(".pyc"):
fname = fname[0:-1]
self.state.document.settings.record_dependencies.add(fname)
# We simply format out the documentation as rst then feed it back
# into the parser for conversion. We don't even emit ourselves, so
# there's no record of us.
self.state_machine.insert_input(format_module(module), fname)
return []
class Searchfox(ReferenceRole):
"""Role which links a relative path from the source to it's searchfox URL.
Can be used like:
See :searchfox:`browser/base/content/browser-places.js` for more details.
Will generate a link to
The example above will use the path as the text, to use custom text:
See :searchfox:`this file <browser/base/content/browser-places.js>` for
more details.
To specify a different source tree:
See :searchfox:`mozilla-beta:browser/base/content/browser-places.js`
for more details.
"""
def run(self):
if ":" in self.target:
source, path = self.target.split(":", 1)
else:
source = "mozilla-central"
path = self.target
url = base.format(source=source, path=path)
if self.has_explicit_title:
title = self.title
else:
title = path
node = nodes.reference(self.rawtext, title, refuri=url, **self.options)
return [node], []
def setup(app):
from moztreedocs import manager
app.add_directive("mozbuildsymbols", MozbuildSymbols)
app.add_role("searchfox", Searchfox())
# Unlike typical Sphinx installs, our documentation is assembled from
# many sources and staged in a common location. This arguably isn't a best
# practice, but it was the easiest to implement at the time.
#
# Here, we invoke our custom code for staging/generating all our
# documentation.
# Export and write "governance" documentation to disk.
config_path = find_mots_config_path(app)
if config_path:
export_mots(config_path)
manager.generate_docs(app)
app.srcdir = Path(manager.staging_dir)