|
23 | 23 | ) |
24 | 24 | from sphinx.ext.autodoc._member_finder import _filter_members, _get_members_to_document |
25 | 25 | from sphinx.ext.autodoc._renderer import ( |
| 26 | + _add_content, |
26 | 27 | _directive_header_lines, |
27 | 28 | _hide_value_re, |
28 | 29 | ) |
@@ -545,42 +546,170 @@ def get_sourcename(self) -> str: |
545 | 546 |
|
546 | 547 | def add_content(self, more_content: StringList | None) -> None: |
547 | 548 | """Add content from docstrings, attribute documentation and user.""" |
548 | | - docstring = True |
549 | | - |
550 | | - # set sourcename and add content from attribute documentation |
551 | | - sourcename = self.get_sourcename() |
552 | | - if self.analyzer: |
553 | | - attr_docs = self.analyzer.find_attr_docs() |
554 | | - if self.props.parts: |
555 | | - key = ('.'.join(self.props.parts[:-1]), self.props.parts[-1]) |
556 | | - if key in attr_docs: |
557 | | - docstring = False |
558 | | - # make a copy of docstring for attributes to avoid cache |
559 | | - # the change of autodoc-process-docstring event. |
560 | | - attribute_docstrings = [list(attr_docs[key])] |
561 | | - |
562 | | - for i, line in enumerate(self.process_doc(attribute_docstrings)): |
563 | | - self.add_line(line, sourcename, i) |
564 | | - |
565 | 549 | # add content from docstrings |
566 | | - if docstring: |
567 | | - docstrings = self.get_doc() |
568 | | - if docstrings is None: |
569 | | - # Do not call autodoc-process-docstring on get_doc() returns None. |
570 | | - pass |
571 | | - else: |
572 | | - if not docstrings: |
573 | | - # append at least a dummy docstring, so that the event |
574 | | - # autodoc-process-docstring is fired and can add some |
575 | | - # content if desired |
576 | | - docstrings.append([]) |
577 | | - for i, line in enumerate(self.process_doc(docstrings)): |
578 | | - self.add_line(line, sourcename, i) |
| 550 | + processed_doc = StringList( |
| 551 | + list( |
| 552 | + self._process_docstrings( |
| 553 | + self._get_docstrings() or [], |
| 554 | + events=self._events, |
| 555 | + props=self.props, |
| 556 | + obj=self.props._obj, |
| 557 | + options=self.options, |
| 558 | + ) |
| 559 | + ), |
| 560 | + source=self.get_sourcename(), |
| 561 | + ) |
| 562 | + _add_content( |
| 563 | + processed_doc, |
| 564 | + result=self.directive.result, |
| 565 | + indent=self.indent + ' ' * (self.props.obj_type == 'module'), |
| 566 | + ) |
579 | 567 |
|
580 | 568 | # add additional content (e.g. from document), if present |
581 | | - if more_content: |
582 | | - for line, src in zip(more_content.data, more_content.items, strict=True): |
583 | | - self.add_line(line, src[0], src[1]) |
| 569 | + more_content = self._assemble_more_content( |
| 570 | + more_content=StringList() if more_content is None else more_content, |
| 571 | + typehints_format=self.config.autodoc_typehints_format, |
| 572 | + python_display_short_literal_types=self.config.python_display_short_literal_types, |
| 573 | + props=self.props, |
| 574 | + ) |
| 575 | + _add_content( |
| 576 | + more_content, |
| 577 | + result=self.directive.result, |
| 578 | + indent=self.indent, |
| 579 | + ) |
| 580 | + |
| 581 | + def _get_docstrings(self) -> list[list[str]] | None: |
| 582 | + """Add content from docstrings, attribute documentation and user.""" |
| 583 | + docstrings = self.get_doc() |
| 584 | + attr_docs = None if self.analyzer is None else self.analyzer.find_attr_docs() |
| 585 | + props = self.props |
| 586 | + |
| 587 | + if docstrings is not None and len(docstrings) == 0: |
| 588 | + # append at least a dummy docstring, so that the event |
| 589 | + # autodoc-process-docstring is fired and can add some |
| 590 | + # content if desired |
| 591 | + docstrings.append([]) |
| 592 | + |
| 593 | + if props.obj_type in {'data', 'attribute'}: |
| 594 | + return docstrings |
| 595 | + |
| 596 | + if props.obj_type in {'class', 'exception'}: |
| 597 | + real_module = props._obj___module__ or props.module_name |
| 598 | + if props.module_name != real_module: |
| 599 | + try: |
| 600 | + # override analyzer to obtain doc-comment around its definition. |
| 601 | + ma = ModuleAnalyzer.for_module(props.module_name) |
| 602 | + ma.analyze() |
| 603 | + attr_docs = ma.attr_docs |
| 604 | + except PycodeError: |
| 605 | + pass |
| 606 | + |
| 607 | + # add content from attribute documentation |
| 608 | + if attr_docs is not None and props.parts: |
| 609 | + key = ('.'.join(props.parent_names), props.name) |
| 610 | + if key in attr_docs: |
| 611 | + # make a copy of docstring for attributes to avoid cache |
| 612 | + # the change of autodoc-process-docstring event. |
| 613 | + return [list(attr_docs[key])] |
| 614 | + |
| 615 | + return docstrings |
| 616 | + |
| 617 | + @staticmethod |
| 618 | + def _process_docstrings( |
| 619 | + docstrings: list[list[str]], |
| 620 | + *, |
| 621 | + events: EventManager, |
| 622 | + props: _ItemProperties, |
| 623 | + obj: Any, |
| 624 | + options: _AutoDocumenterOptions, |
| 625 | + ) -> Iterator[str]: |
| 626 | + """Let the user process the docstrings before adding them.""" |
| 627 | + for docstring_lines in docstrings: |
| 628 | + # let extensions preprocess docstrings |
| 629 | + events.emit( |
| 630 | + 'autodoc-process-docstring', |
| 631 | + props.obj_type, |
| 632 | + props.full_name, |
| 633 | + obj, |
| 634 | + options, |
| 635 | + docstring_lines, |
| 636 | + ) |
| 637 | + |
| 638 | + if docstring_lines and docstring_lines[-1]: |
| 639 | + # append a blank line to the end of the docstring |
| 640 | + docstring_lines.append('') |
| 641 | + |
| 642 | + yield from docstring_lines |
| 643 | + |
| 644 | + @staticmethod |
| 645 | + def _assemble_more_content( |
| 646 | + more_content: StringList, |
| 647 | + *, |
| 648 | + props: _ItemProperties, |
| 649 | + typehints_format: Literal['fully-qualified', 'short'], |
| 650 | + python_display_short_literal_types: bool, |
| 651 | + ) -> StringList: |
| 652 | + """Add content from docstrings, attribute documentation and user.""" |
| 653 | + obj = props._obj |
| 654 | + |
| 655 | + if props.obj_type in {'data', 'attribute'}: |
| 656 | + mode = _get_render_mode(typehints_format) |
| 657 | + |
| 658 | + # Support for documenting GenericAliases |
| 659 | + if inspect.isgenericalias(obj): |
| 660 | + alias = restify(obj, mode=mode) |
| 661 | + more_content.append(_('alias of %s') % alias, '') |
| 662 | + more_content.append('', '') |
| 663 | + return more_content |
| 664 | + |
| 665 | + if props.obj_type in {'class', 'exception'}: |
| 666 | + from sphinx.ext.autodoc._property_types import _ClassDefProperties |
| 667 | + |
| 668 | + assert isinstance(props, _ClassDefProperties) |
| 669 | + |
| 670 | + mode = _get_render_mode(typehints_format) |
| 671 | + |
| 672 | + if isinstance(obj, NewType): |
| 673 | + supertype = restify(obj.__supertype__, mode=mode) |
| 674 | + return StringList([_('alias of %s') % supertype, ''], source='') |
| 675 | + |
| 676 | + if isinstance(obj, TypeVar): |
| 677 | + short_literals = python_display_short_literal_types |
| 678 | + attrs = [ |
| 679 | + repr(obj.__name__), |
| 680 | + *( |
| 681 | + stringify_annotation( |
| 682 | + constraint, mode, short_literals=short_literals |
| 683 | + ) |
| 684 | + for constraint in obj.__constraints__ |
| 685 | + ), |
| 686 | + ] |
| 687 | + if obj.__bound__: |
| 688 | + attrs.append(rf'bound=\ {restify(obj.__bound__, mode=mode)}') |
| 689 | + if obj.__covariant__: |
| 690 | + attrs.append('covariant=True') |
| 691 | + if obj.__contravariant__: |
| 692 | + attrs.append('contravariant=True') |
| 693 | + |
| 694 | + alias = f'TypeVar({", ".join(attrs)})' |
| 695 | + return StringList([_('alias of %s') % alias, ''], source='') |
| 696 | + |
| 697 | + if props.doc_as_attr: |
| 698 | + try: |
| 699 | + analyzer = ModuleAnalyzer.for_module(props.module_name) |
| 700 | + analyzer.analyze() |
| 701 | + key = ('', props.dotted_parts) |
| 702 | + no_classvar_doc_comment = key not in analyzer.attr_docs |
| 703 | + except PycodeError: |
| 704 | + no_classvar_doc_comment = True |
| 705 | + |
| 706 | + if no_classvar_doc_comment: |
| 707 | + alias = restify(obj, mode=mode) |
| 708 | + return StringList([_('alias of %s') % alias], source='') |
| 709 | + |
| 710 | + return more_content |
| 711 | + |
| 712 | + return more_content |
584 | 713 |
|
585 | 714 | def sort_members( |
586 | 715 | self, documenters: list[tuple[Documenter, bool]], order: str |
@@ -863,15 +992,6 @@ def __init__(self, *args: Any) -> None: |
863 | 992 | self.options = self.options.merge_member_options() |
864 | 993 | self.__all__: Sequence[str] | None = None |
865 | 994 |
|
866 | | - def add_content(self, more_content: StringList | None) -> None: |
867 | | - old_indent = self.indent |
868 | | - self.indent += self._extra_indent |
869 | | - super().add_content(None) |
870 | | - self.indent = old_indent |
871 | | - if more_content: |
872 | | - for line, src in zip(more_content.data, more_content.items, strict=True): |
873 | | - self.add_line(line, src[0], src[1]) |
874 | | - |
875 | 995 | @classmethod |
876 | 996 | def can_document_member( |
877 | 997 | cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any |
@@ -1435,50 +1555,6 @@ def get_variable_comment(self) -> list[str] | None: |
1435 | 1555 | except PycodeError: |
1436 | 1556 | return None |
1437 | 1557 |
|
1438 | | - def add_content(self, more_content: StringList | None) -> None: |
1439 | | - mode = _get_render_mode(self.config.autodoc_typehints_format) |
1440 | | - short_literals = self.config.python_display_short_literal_types |
1441 | | - |
1442 | | - if isinstance(self.props._obj, NewType): |
1443 | | - supertype = restify(self.props._obj.__supertype__, mode=mode) |
1444 | | - |
1445 | | - more_content = StringList([_('alias of %s') % supertype, ''], source='') |
1446 | | - if isinstance(self.props._obj, TypeVar): |
1447 | | - attrs = [repr(self.props._obj.__name__)] |
1448 | | - attrs.extend( |
1449 | | - stringify_annotation(constraint, mode, short_literals=short_literals) |
1450 | | - for constraint in self.props._obj.__constraints__ |
1451 | | - ) |
1452 | | - if self.props._obj.__bound__: |
1453 | | - bound = restify(self.props._obj.__bound__, mode=mode) |
1454 | | - attrs.append(r'bound=\ ' + bound) |
1455 | | - if self.props._obj.__covariant__: |
1456 | | - attrs.append('covariant=True') |
1457 | | - if self.props._obj.__contravariant__: |
1458 | | - attrs.append('contravariant=True') |
1459 | | - |
1460 | | - more_content = StringList( |
1461 | | - [_('alias of TypeVar(%s)') % ', '.join(attrs), ''], source='' |
1462 | | - ) |
1463 | | - if self.props.doc_as_attr and self.props.module_name != ( |
1464 | | - self.props._obj___module__ or self.props.module_name |
1465 | | - ): |
1466 | | - try: |
1467 | | - # override analyzer to obtain doccomment around its definition. |
1468 | | - self.analyzer = ModuleAnalyzer.for_module(self.props.module_name) |
1469 | | - self.analyzer.analyze() |
1470 | | - except PycodeError: |
1471 | | - pass |
1472 | | - |
1473 | | - if self.props.doc_as_attr and not self.get_variable_comment(): |
1474 | | - try: |
1475 | | - alias = restify(self.props._obj, mode=mode) |
1476 | | - more_content = StringList([_('alias of %s') % alias], source='') |
1477 | | - except AttributeError: |
1478 | | - pass # Invalid class object is passed. |
1479 | | - |
1480 | | - super().add_content(more_content) |
1481 | | - |
1482 | 1558 | def generate( |
1483 | 1559 | self, |
1484 | 1560 | more_content: StringList | None = None, |
@@ -1579,21 +1655,6 @@ def get_doc(self) -> list[list[str]] | None: |
1579 | 1655 | else: |
1580 | 1656 | return super().get_doc() |
1581 | 1657 |
|
1582 | | - def add_content(self, more_content: StringList | None) -> None: |
1583 | | - # Disable analyzing variable comment on Documenter.add_content() to control it on |
1584 | | - # DataDocumenter.add_content() |
1585 | | - self.analyzer = None |
1586 | | - |
1587 | | - if not more_content: |
1588 | | - more_content = StringList() |
1589 | | - |
1590 | | - _add_content_generic_alias_( |
1591 | | - more_content, |
1592 | | - self.props._obj, |
1593 | | - autodoc_typehints_format=self.config.autodoc_typehints_format, |
1594 | | - ) |
1595 | | - super().add_content(more_content) |
1596 | | - |
1597 | 1658 |
|
1598 | 1659 | class MethodDocumenter(Documenter): |
1599 | 1660 | """Specialized Documenter subclass for methods (normal, static and class).""" |
@@ -1947,20 +2008,6 @@ def get_doc(self) -> list[list[str]] | None: |
1947 | 2008 | finally: |
1948 | 2009 | self.config.autodoc_inherit_docstrings = orig |
1949 | 2010 |
|
1950 | | - def add_content(self, more_content: StringList | None) -> None: |
1951 | | - # Disable analyzing attribute comment on Documenter.add_content() to control it on |
1952 | | - # AttributeDocumenter.add_content() |
1953 | | - self.analyzer = None |
1954 | | - |
1955 | | - if more_content is None: |
1956 | | - more_content = StringList() |
1957 | | - _add_content_generic_alias_( |
1958 | | - more_content, |
1959 | | - self.props._obj, |
1960 | | - autodoc_typehints_format=self.config.autodoc_typehints_format, |
1961 | | - ) |
1962 | | - super().add_content(more_content) |
1963 | | - |
1964 | 2011 |
|
1965 | 2012 | class PropertyDocumenter(Documenter): |
1966 | 2013 | """Specialized Documenter subclass for properties.""" |
@@ -2069,19 +2116,6 @@ def autodoc_attrgetter( |
2069 | 2116 | return safe_getattr(obj, name, *defargs) |
2070 | 2117 |
|
2071 | 2118 |
|
2072 | | -def _add_content_generic_alias_( |
2073 | | - more_content: StringList, |
2074 | | - /, |
2075 | | - obj: object, |
2076 | | - autodoc_typehints_format: Literal['fully-qualified', 'short'], |
2077 | | -) -> None: |
2078 | | - """Support for documenting GenericAliases.""" |
2079 | | - if inspect.isgenericalias(obj): |
2080 | | - alias = restify(obj, mode=_get_render_mode(autodoc_typehints_format)) |
2081 | | - more_content.append(_('alias of %s') % alias, '') |
2082 | | - more_content.append('', '') |
2083 | | - |
2084 | | - |
2085 | 2119 | def _document_members( |
2086 | 2120 | *, |
2087 | 2121 | member_documenters: list[tuple[Documenter, bool]], |
|
0 commit comments