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
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
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)