Add Python module documentation generator

This commit is contained in:
2026-04-25 19:34:34 +02:00
parent ecd3f7e76b
commit a17e56fb3d
18 changed files with 1097 additions and 6 deletions
+81 -5
View File
@@ -13,8 +13,8 @@
# limitations under the License.
from __future__ import annotations
from ...utils import ExistingDirectory, assert_that_is_instance, ExistingFile, non_empty_str
from ...exceptions import PythonArgumentStructureError
from ...utils import ExistingDirectory, assert_that_is_instance, ExistingFile, non_empty_str, get_relative_path_segments
from ...exceptions import PythonArgumentStructureError, InvalidNamespaceError
from .namespaces import PythonModuleNamespace, PythonPackageNamespace
from .definitions import PythonDefinition, PythonFunctionDefinition, PythonAsyncFunctionDefinition, PythonClassDefinition
from .arguments import PythonArgumentKind, PythonFunctionArgument
@@ -26,6 +26,7 @@ from enum import Enum
import docstring_parser
import pathlib
import ast
import types
__all__ = [
'PythonDefinitionDocumentationIncludeSections',
@@ -255,6 +256,7 @@ class _NoDocstringTemplate:
many_returns = []
deprecation = None
examples = []
meta = []
short_description = None
long_description = None
blank_after_short_description = None
@@ -408,8 +410,8 @@ class PythonDefinitionDocumentationGenerator:
self,
definition: PythonDefinition,
include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(),
*,
level: int = 1,
*,
allow_html: bool = True,
allow_tables: bool = False,
skip_empty_sections: bool = True,
@@ -741,12 +743,49 @@ class PythonModuleDocumentationGenerator:
:param module_path: The path of the module file.
:type module_path: ExistingFile
:param src_directory: The directory of the source code. It is
optional and only used for making the module
identifiers in the documentation relative.
:type src_directory: Union[ExistingDirectory, None]
:param include_sections: The sections that will be included.
:type include_sections: PythonDefinitionDocumentationIncludeSections
:kwargs: The options for ``PythonDefinitionDocumentationGenerator``.
'''
def __init__(self, module_path: ExistingFile) -> None:
def __init__(
self,
module_path: ExistingFile,
src_directory: Union[ExistingDirectory, None] = None,
include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(),
**kwargs
) -> None:
assert_that_is_instance(module_path, ExistingFile)
assert_that_is_instance(src_directory, (ExistingDirectory, types.NoneType))
if module_path.name.count('.') > 1:
raise InvalidNamespaceError('namespace identifier cannot contain more than one dot', namespace_identifier = module_path.name)
self.module_path = pathlib.Path(str(module_path))
self.src_directory = pathlib.Path(str(src_directory)) if src_directory is not None else None
self.include_sections = include_sections
self.kwargs = kwargs
self._source_code_cache = None
self._ast_cache = None
@property
def _source_code(self) -> str:
if self._source_code_cache is None:
self._source_code_cache = self.module_path.read_text()
return self._source_code_cache
@property
def _ast(self) -> str:
if self._ast_cache is None:
self._ast_cache = ast.parse(self._source_code)
return self._ast_cache
def collect_definitions(self) -> Iterator[PythonDefinition]:
'''
@@ -756,10 +795,47 @@ class PythonModuleDocumentationGenerator:
:rtype: Iterator[PythonDefinition]
'''
tree = ast.parse(self.module_path.read_text())
tree = self._ast
return _collect_definitions(tree)
def generate_documentation(self) -> str:
'''
Generates the documentation in the markdown format.
:return: The documentation.
:rtype: str
'''
md = '# Module `'
if self.src_directory is not None:
module_identifier = '.'.join(get_relative_path_segments(self.module_path, self.src_directory))
if module_identifier.endswith('.py'):
md += module_identifier[:-3]
else:
md += module_identifier
else:
md += self.module_path.stem
md += '`\n\n'
docstring = ast.get_docstring(self._ast)
if docstring is not None:
md += docstring + '\n\n'
for definition in self.collect_definitions():
md += PythonDefinitionDocumentationGenerator(
definition,
self.include_sections,
2,
**self.kwargs
).generate_documentation() + '\n\n'
return md.strip()
class PythonDocumentationGenerator:
'''
The class for the documentation generator.