最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How to combine MkDocs with pybind11 modules? - Stack Overflow

programmeradmin3浏览0评论

I'm trying to generate a documentation site for a Python API which is created with pybind11 using MkDocs. This API has also a .cpp API which is generated well.

This is my C++ common header code:

#include <pybind11/pybind11.h>
namespace App::Binding
{
namespace py = pybind11;
}

and the API for the above application is defined in this C++ header + .cpp files: H file:

#pragma once
#include "PybindCommon.h"

namespace App::Binding
{
/**
 * @brief Bindings for IApp API
 */
void bindApp(py::module_& m);
}    // namespace App::Binding

CPP file:

#include "IApp.h"
#include "AppBindings.h"

#include <pybind11/stl.h>


/**
 * @brief Bindings for IApp API
 */
namespace App::Binding
{
// Custom deleter for unique_ptr<IApp> objects to call release() on the object when the object is GC'd in Python
struct AppDeleter
{
  void operator()(IApp* app)
  {
    if(app) {
      app->release();
    }
  }
};

void bindApp(py::module_& m)
{
  // python constructor will be binded to a unique_ptr<IApp> object with a custom deleter on C++ side
  py::class_<IApp, std::unique_ptr<IApp, AppDeleter>>(m, "App",
    R"pbdoc(
    The main entry point for accessing the App.
    Exposes general app metadata objects for accessing specific data from the app.
    )pbdoc")
    .def(py::init([](const char* config_path, LibType lib_type) {
           IApp* app = initApp(config_path, lib_type);
           if(app == nullptr) {
             throw std::runtime_error(
               "Can only construct one app per process. Multi-app support planned in future versions.");
           }
           return std::unique_ptr<IApp, AppDeleter>(app);
         }),
         py::arg("config_path"),
         py::arg("lib"),
         R"pbdoc(
            Create an app object

            Args:
                config_path (:obj:`str`): 
                    Path to the configuration file for the app.
                lib (:obj:`LibType`): 
                    Type of shared-object to use the app with.

            Returns:
                :obj:`App`: 
                    App object.

            Raises:
                RuntimeError: If an App was already previously created in the same process. Will be fixed in future versions.
          )pbdoc")
    .def(
      "get_range",
      [](const IApp& self) {
        uint32_t first, last;
        self.getRange(first, last);
        return py::make_tuple(first, last);
      },
      R"pbdoc(
        Get the App legal range

        Returns:
            :obj:`tuple`: 
                A tuple containing:
                  - first (:obj:`int`): 
                      First range value.
                  - last (:obj:`int`): 
                      Last range value.
        )pbdoc")
    .def("get_version",
         &IApp::getVersion,
         R"pbdoc(
          Get the created App version

          Returns:
              :obj:`str`: 
                  SHA of the App version.
          )pbdoc")
    .def("get_fps",
         &IApp::getAppFps,
         R"pbdoc(
          Get the FPS of the App

          Returns:
              :obj:`int`: 
                  App's system FPS.
          )pbdoc")
    .def("get_available_frames",
         &IApp::getAvailableFrames,
         R"pbdoc(
          Get the available frames for the primary app sensor

          Returns:
              :obj:`list`: 
                  List of available frames for the primary sensor.
          )pbdoc")
    .def("get_available_frame_configs",
         &IApp::getAvailableFrameConfigs,
         py::arg("frame"),
         R"pbdoc(
          Get all the available configurations for a specific frame type.

          Args:
              frame (:obj:`FrameType`): 
                  Type of frame.

          Returns:
              :obj:`list`: 
                  List of available configurations.
          )pbdoc");
}
}    // namespace App::Binding

I've been using Sphinx with Doxygen up until now to create static HTML files and generate them to a local site with the API, and my office started using MkDocs (which is of course much nicer), and now I need to somehow extract the API above using an mkdocs.yaml file.

What I've tried so far:

  • Using mkdcostrings plugin with a python handler where the input is the .so created by pybind (which contains the docstrings extracted by pybind_module::doc()).
  • Using Sphinx as a pre-processor with sphinx-build -M xml and using the output XML files as input to mkdoxy plugin.
  • Using the above with sphinx-build -M json and mkdocsrings plugin- created .fjson files that didn't parse well by mkdocstrings.
  • Parsing the .cpp in a Python script trying to create .py files with regular docstrings- not a good solution for using CI automation (+ hard to maintain).
  • I tried some more small attempts with markdown-extensions that didn't work because of Jinja2 syntax or {eval-rst} issues I couldn't figure out.

This is my current mkdocs.yaml file configuration I've come up with until now:

site_name: "App Docs"
repo_url: <company_site>/app.git
repo_name: app
edit_uri: edit/master/include/

dev_addr: 0.0.0.0:38000
docs_dir: .

plugins:
  - mkdoxy:
      projects:
        App:
          src-dirs: include
          doxy-cfg:
            FILE_PATTERNS: "*.cpp *.h* *.md"
            RECURSIVE: YES
            INPUT: include
            GENERATE_XML: YES
            EXTRACT_ALL: YES
            OPTIMIZE_OUTPUT_FOR_C: YES
        pyDAST:
          src-dirs: docs/build/json/python
          doxy-cfg:
            FILE_PATTERNS: "*.fjson"
            RECURSIVE: NO
            INPUT: docs/build/json/python
            GENERATE_XML: YES
            EXTRACT_ALL: YES
            OPTIMIZE_OUTPUT_FOR_JAVA: YES
      save-api: .mkdoxy
      full-doc: True
      debug: False
      ignore-errors: False
  - search
  - mkdocstrings
  - open-in-new-tab
  - autorefs
  - same-dir
  - awesome-pages
  - include-markdown

theme:
  name: material
  features:
    - navigation.tabs
    - navigation.tabs.sticky
    - navigation.indexes
    - navigation.path
    - navigation.top
    - navigation.tracking
  palette:
    - media: "(prefers-color-scheme: dark)"
      scheme: default
      primary: blue
      accent: indigo
      toggle:
        icon: material/toggle-switch
        name: Switch to light mode
    - media: "(prefers-color-scheme: light)"
      scheme: slate
      toggle:
        icon: material/toggle-switch-off-outline
        name: Switch to dark mode

extra_javascript:
  - @main/mkdocs/javascripts/massilia-graphviz.js

markdown_extensions:
  - extra
  - tables
  - mkdocs_graphviz
  - attr_list
  - mdx_truly_sane_lists
  - mdx_math:
      use_gitlab_delimiters: True # for $`...`$ style math
  - toc:
      permalink: true
  - pymdownx.highlight
  - pymdownx.superfences
  - def_list
  - admonition
  - pymdownx.details
  - markdown.extensions.md_in_html
  - pymdownx.snippets:
      check_paths: true
  - pymdownx.blocks.admonition:
      types:
        - new
        - settings
        - note
        - abstract
        - info
        - tip
        - success
        - question
        - warning
        - failure
        - danger
        - bug
        - example
        - quote
  - pymdownx.blocks.details:
  - pymdownx.blocks.html:
  - pymdownx.blocks.definition:
  - pymdownx.blocks.tab:
  - pymdownx.tabbed:
      alternate_style: true
  - pymdownx.emoji:
      emoji_index: !!python/name:material.extensions.emoji.twemoji
      emoji_generator: !!python/name:materialx.emoji.to_svg


use_directory_urls: True

nav: ...

Is someone familiar with some sort of plugin or method to combine these two?

发布评论

评论列表(0)

  1. 暂无评论