generate_docs.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/env python
  2. # Protocol Buffers - Google's data interchange format
  3. # Copyright 2008 Google Inc. All rights reserved.
  4. # https://developers.google.com/protocol-buffers/
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. #
  10. # * Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # * Redistributions in binary form must reproduce the above
  13. # copyright notice, this list of conditions and the following disclaimer
  14. # in the documentation and/or other materials provided with the
  15. # distribution.
  16. # * Neither the name of Google Inc. nor the names of its
  17. # contributors may be used to endorse or promote products derived from
  18. # this software without specific prior written permission.
  19. #
  20. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. """Script to generate a list of all modules to use in autosummary.
  32. This script creates a ReStructured Text file for each public module in the
  33. protobuf Python package. The script also updates the table of contents in
  34. ``docs/index.rst`` to point to these module references.
  35. To build the docs with Sphinx:
  36. 1. Install the needed packages (``sphinx``, ``sphinxcontrib-napoleon`` for
  37. Google-style docstring support). I've created a conda environment file to
  38. make this easier:
  39. .. code:: bash
  40. conda env create -f python/docs/environment.yml
  41. 2. (Optional) Generate reference docs files and regenerate index:
  42. .. code:: bash
  43. cd python/docs
  44. python generate_docs.py
  45. 3. Run Sphinx.
  46. .. code:: bash
  47. make html
  48. """
  49. import pathlib
  50. import re
  51. DOCS_DIR = pathlib.Path(__file__).parent.resolve()
  52. PYTHON_DIR = DOCS_DIR.parent
  53. SOURCE_DIR = PYTHON_DIR / "google" / "protobuf"
  54. SOURCE_POSIX = SOURCE_DIR.as_posix()
  55. # Modules which are always included:
  56. INCLUDED_MODULES = (
  57. "google.protobuf.internal.containers",
  58. )
  59. # Packages to ignore, including all modules (unless in INCLUDED_MODULES):
  60. IGNORED_PACKAGES = (
  61. "compiler",
  62. "docs",
  63. "internal",
  64. "pyext",
  65. "util",
  66. )
  67. # Ignored module stems in all packages (unless in INCLUDED_MODULES):
  68. IGNORED_MODULES = (
  69. "any_test_pb2",
  70. "api_pb2",
  71. "unittest",
  72. "source_context_pb2",
  73. "test_messages_proto3_pb2",
  74. "test_messages_proto2",
  75. )
  76. TOC_REGEX = re.compile(
  77. r"\.\. START REFTOC.*\.\. END REFTOC\.\n",
  78. flags=re.DOTALL,
  79. )
  80. TOC_TEMPLATE = """.. START REFTOC, generated by generate_docs.py.
  81. .. toctree::
  82. {toctree}
  83. .. END REFTOC.
  84. """
  85. AUTOMODULE_TEMPLATE = """.. DO NOT EDIT, generated by generate_docs.py.
  86. .. ifconfig:: build_env == 'readthedocs'
  87. .. warning::
  88. You are reading the documentation for the `latest committed changes
  89. <https://github.com/protocolbuffers/protobuf/tree/master/python>`_ of
  90. the `Protocol Buffers package for Python
  91. <https://developers.google.com/protocol-buffers/docs/pythontutorial>`_.
  92. Some features may not yet be released. Read the documentation for the
  93. latest released package at `googleapis.dev
  94. <https://googleapis.dev/python/protobuf/latest/>`_.
  95. {module}
  96. {underline}
  97. .. automodule:: {module}
  98. :members:
  99. :inherited-members:
  100. :undoc-members:
  101. """
  102. def find_modules():
  103. modules = []
  104. for module_path in SOURCE_DIR.glob("**/*.py"):
  105. # Determine the (dotted) relative package and module names.
  106. package_path = module_path.parent.relative_to(PYTHON_DIR)
  107. if package_path == SOURCE_DIR:
  108. package_name = ""
  109. module_name = module_path.stem
  110. else:
  111. package_name = package_path.as_posix().replace("/", ".")
  112. module_name = package_name + "." + module_path.stem
  113. # Filter: first, accept anything in the whitelist; then, reject anything
  114. # at package level, then module name level.
  115. if any(include == module_name for include in INCLUDED_MODULES):
  116. pass
  117. elif any(ignored in package_name for ignored in IGNORED_PACKAGES):
  118. continue
  119. elif any(ignored in module_path.stem for ignored in IGNORED_MODULES):
  120. continue
  121. if module_path.name == "__init__.py":
  122. modules.append(package_name)
  123. else:
  124. modules.append(module_name)
  125. return modules
  126. def write_automodule(module):
  127. contents = AUTOMODULE_TEMPLATE.format(module=module, underline="=" * len(module),)
  128. automodule_path = DOCS_DIR.joinpath(*module.split(".")).with_suffix(".rst")
  129. try:
  130. automodule_path.parent.mkdir(parents=True)
  131. except FileExistsError:
  132. pass
  133. with open(automodule_path, "w") as automodule_file:
  134. automodule_file.write(contents)
  135. def replace_toc(modules):
  136. toctree = [module.replace(".", "/") for module in modules]
  137. with open(DOCS_DIR / "index.rst", "r") as index_file:
  138. index_contents = index_file.read()
  139. toc = TOC_TEMPLATE.format(
  140. toctree="\n ".join(toctree)
  141. )
  142. index_contents = re.sub(TOC_REGEX, toc, index_contents)
  143. with open(DOCS_DIR / "index.rst", "w") as index_file:
  144. index_file.write(index_contents)
  145. def main():
  146. modules = list(sorted(find_modules()))
  147. for module in modules:
  148. print("Generating reference for {}".format(module))
  149. write_automodule(module)
  150. print("Generating index.rst")
  151. replace_toc(modules)
  152. if __name__ == "__main__":
  153. main()