pydoctor

repository·master·Indexed 42 days ago

https://github.com/twisted/pydoctor

A standalone API documentation generator for Python that uses static analysis to create documentation from source code. It is designed to handle complex inheritance and interface implementations, such as zope.interface, and supports docstrings, attribute docstrings, doc-comments, and type annotations.

Tokens
13.9K
Snippets
40
Records
61
Agent score
52%

What's inside pydoctor

  1. What is pydoctor?

    master
    pydoctor is a standalone API documentation generator that uses static analysis. It is designed to handle complex Python features like zope.interface declarations, computing inheritance hierarchies and resolving imports to show which classes implement which interfaces. It was originally developed to replace epydoc for the Twisted project.
  2. Extend pydoctor using the extension mechanism

    master

    If you need to modify the state of objects in the Documentable tree (e.g., transforming how certain classes are parsed), use the extension mechanism instead of overriding the System class.

    An extension is a Python module containing a setup_pydoctor_extension(registrar) function. This function is called during initialization and receives an ExtRegistrar object.

    Extensions can register:

    • AST builder visitors: To modify how the AST is traversed and parsed.
    • Mixin classes: To add functionality to pydoctor.model.Documentable objects.
    • Post processors.
  3. Document properties as attributes

    master

    Methods decorated with @property (or any decorator ending in property or Property) are documented as attributes rather than methods.

    To ensure custom properties are recognized, follow the naming convention of ending the decorator name with property or Property.

    class Knight:
        @property
        def name(self):
            return self._name
    
        @customProperty
        def quest(self):
            return 'Find the object'
  4. Search functionality in generated docs

    master

    The generated documentation includes a client-side search system (based on lunr.js).

    UX Features:

    • Keyboard Shortcuts: Use Ctrl+K or Cmd+K (on Mac) to focus the search box.
    • Navigation: Once focused, use the up and down arrow keys to navigate results and Enter to open a selection.
    • Docstring Search: Prefix a term with docstring: (e.g., docstring:<term>) to search specifically within docstrings.
    • Boolean Logic: Use a logical "and" between terms by default. Use a leading ? to make a term optional.
  5. Compare pydoctor with sphinx-autodoc

    master

    When choosing between pydoctor and sphinx-autodoc, consider the following differences:

    • Automation: pydoctor is fully automatic and generates documentation directly from Python source files. sphinx-autodoc is semi-automatic and requires accompanying reStructuredText files.
    • Output Structure: pydoctor generates a simple, efficient layout with one page per class, module, and package, often using tables to present information. sphinx-autodoc can produce more complex and potentially overwhelming output.
    • Integration: pydoctor output can be seamlessly linked to narrative documentation written in Sphinx.
    • Use Case: pydoctor is particularly well-suited for libraries with significant internal subclassing and provides native support for zope.interface (recognizing both interfaces and the classes that implement them).
  6. Identify constants in documentation

    master

    Pydoctor recognizes constants based on two criteria:

    1. Variables defined with an all-uppercase name (following PEP 8).
    2. Variables using the typing.Final qualifier (available in Python 3.8+).

    For Python 3.6 and 3.7, use typing_extensions.Final instead.

    Constants are rendered with syntax highlighting in the generated documentation.

    from typing import Final
    
    X = 3.14
    y: Final = ['a', 'b']
    
    # For Python 3.6/3.7
    from typing_extensions import Final
    z: Final = 'relative/path'
  7. Understanding Private and Public API visibility

    master

    Pydoctor uses naming conventions to determine the visibility of modules, classes, and functions:

    • Private: Names starting with a single underscore (e.g., _Private) are considered private. They are hidden by default in the generated documentation but can be revealed via a button in the UI.
    • Public: Standard names are considered public.
    • Dunders: Names that start and end with double underscores (e.g., __str__, __eq__) are always considered public.

    Note: Pydoctor supports three levels of privacy: public, private, and hidden. For advanced customization, see the privacy override documentation.

    class _Private:
        """This class won't be shown unless explicitly revealed."""
    
    class Public:
        """This class is public, but some of its methods are private."""
    
        def public(self):
            """This is a public method."""
    
        def _private(self):
            """For internal use only."""
    
        def __eq__(self, other):
            """Is this object equal to 'other'?
    
            This method is public.
            """
  8. How pydoctor handles branch priorities (try/except and TYPE_CHECKING)

    master

    Pydoctor uses a priority system to resolve names when dealing with conditional blocks:

    Try/Except/Else Blocks

    Names defined in the main flow take precedence over definitions in except or else blocks. If a name is defined in the main flow, the version in the except block is ignored.

    TYPE_CHECKING Blocks

    By default, pydoctor prioritizes the main flow over the TYPE_CHECKING block. If a class or type is defined in both, the runtime version is documented and the TYPE_CHECKING version is ignored.

    Tip: Inverting conditions If you want pydoctor to document the types defined inside a TYPE_CHECKING block (e.g., for Protocols), you should invert the condition so the TYPE_CHECKING block becomes the 'main flow':

    if not TYPE_CHECKING:
        from ._implementation import Thing
    else:
        from ._typing import Thing  # This will now be the 'main flow' and be documented
    from typing import TYPE_CHECKING
    if TYPE_CHECKING:
        from typing import Protocol
        class CapSys(Protocol):
            def readouterr() -> Any: ...
    else:
        class CapSys(object):
            ...
    # In the above, CapSys(object) is documented because it is the 'main flow'.
    # To document the Protocol, use:
    if not TYPE_CHECKING:
        from ._implementation import Thing
    else:
        from ._typing import Thing
  9. Use docstrings for module, class, and function documentation

    master

    In Python, you can document modules, classes, and functions using docstrings (triple-quoted strings at the top of the definition). For long docstrings, start with a short summary line, followed by an empty line, and then the detailed description. This ensures the summary is used in summary tables while the detail is shown in the full documentation.

    To include literal escape sequences like \n in your documentation, either escape them twice (\\n) or use a raw string literal (r"""...""").

    """This docstring describes the purpose of this module."""
    
    class C:
        """This docstring describes the purpose of this class."""
    
        def m(self):
            """This docstring describes the purpose of this method."""
            pass
    
    # Long docstring pattern
    def f():
        r"""This line is used as the summary.
    
        More detail about the workings of this function can be added here.
        """
        pass
  10. Re-exporting modules for a clean Public API

    master

    If you want to keep your implementation in private modules but expose a clean public API from a single module, use pydoctor's re-export feature.

    To re-export an element, import it into your top-level module (e.g., __init__.py) and include its name in the __all__ special variable. Pydoctor will then move the documentation for that element to the module where it was re-exported.

    Example Structure:

    • my_project/__init__.py (Re-exports MyClass)
    • my_project/core/_impl.py (Defines and documents MyClass)

    Implementation in my_project/__init__.py:

    from .core._impl import MyClass
    
    __all__ = ("MyClass",)
    from .core._impl import MyClass
    
    __all__ = ("MyClass",)
  11. Document attributes using attribute docstrings

    master

    Pydoctor supports attribute docstrings, which are strings placed immediately after an attribute assignment. Note that these are not part of the standard Python language and are not available at runtime.

    You can document module-level constants, class variables, or instance variables using this method.

    CONST = 123
    """This docstring describes a module level constant."""
    
    class C:
        cvar = None
        """This docstring describes a class variable."""
    
        def __init__(self):
            self.ivar = [];"""It can also be used inline."""