mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 05:05:11 +00:00
Provide more control over automatic generation of docstrings (#486)
Added the docstring_options class, which gives global control over the generation of docstrings and function signatures.
This commit is contained in:
parent
617fbcfc1e
commit
9a110e6da8
@ -160,6 +160,7 @@ set(PYBIND11_HEADERS
|
|||||||
include/pybind11/common.h
|
include/pybind11/common.h
|
||||||
include/pybind11/complex.h
|
include/pybind11/complex.h
|
||||||
include/pybind11/descr.h
|
include/pybind11/descr.h
|
||||||
|
include/pybind11/options.h
|
||||||
include/pybind11/eigen.h
|
include/pybind11/eigen.h
|
||||||
include/pybind11/eval.h
|
include/pybind11/eval.h
|
||||||
include/pybind11/functional.h
|
include/pybind11/functional.h
|
||||||
|
@ -202,5 +202,28 @@ work, it is important that all lines are indented consistently, i.e.:
|
|||||||
----------
|
----------
|
||||||
)mydelimiter");
|
)mydelimiter");
|
||||||
|
|
||||||
|
By default, pybind11 automatically generates and prepends a signature to the docstring of a function
|
||||||
|
registered with ``module::def()`` and ``class_::def()``. Sometimes this
|
||||||
|
behavior is not desirable, because you want to provide your own signature or remove
|
||||||
|
the docstring completely to exclude the function from the Sphinx documentation.
|
||||||
|
The class ``options`` allows you to selectively suppress auto-generated signatures:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
PYBIND11_PLUGIN(example) {
|
||||||
|
py::module m("example", "pybind11 example plugin");
|
||||||
|
|
||||||
|
py::options options;
|
||||||
|
options.disable_function_signatures();
|
||||||
|
|
||||||
|
m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers");
|
||||||
|
|
||||||
|
return m.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that changes to the settings affect only function bindings created during the
|
||||||
|
lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function,
|
||||||
|
the default settings are restored to prevent unwanted side effects.
|
||||||
|
|
||||||
.. [#f4] http://www.sphinx-doc.org
|
.. [#f4] http://www.sphinx-doc.org
|
||||||
.. [#f5] http://github.com/pybind/python_example
|
.. [#f5] http://github.com/pybind/python_example
|
||||||
|
65
include/pybind11/options.h
Normal file
65
include/pybind11/options.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
pybind11/options.h: global settings that are configurable at runtime.
|
||||||
|
|
||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
NAMESPACE_BEGIN(pybind11)
|
||||||
|
|
||||||
|
class options {
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Default RAII constructor, which leaves settings as they currently are.
|
||||||
|
options() : previous_state(global_state()) {}
|
||||||
|
|
||||||
|
// Class is non-copyable.
|
||||||
|
options(const options&) = delete;
|
||||||
|
options& operator=(const options&) = delete;
|
||||||
|
|
||||||
|
// Destructor, which restores settings that were in effect before.
|
||||||
|
~options() {
|
||||||
|
global_state() = previous_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setter methods (affect the global state):
|
||||||
|
|
||||||
|
options& disable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = false; return *this; }
|
||||||
|
|
||||||
|
options& enable_user_defined_docstrings() & { global_state().show_user_defined_docstrings = true; return *this; }
|
||||||
|
|
||||||
|
options& disable_function_signatures() & { global_state().show_function_signatures = false; return *this; }
|
||||||
|
|
||||||
|
options& enable_function_signatures() & { global_state().show_function_signatures = true; return *this; }
|
||||||
|
|
||||||
|
// Getter methods (return the global state):
|
||||||
|
|
||||||
|
static bool show_user_defined_docstrings() { return global_state().show_user_defined_docstrings; }
|
||||||
|
|
||||||
|
static bool show_function_signatures() { return global_state().show_function_signatures; }
|
||||||
|
|
||||||
|
// This type is not meant to be allocated on the heap.
|
||||||
|
void* operator new(size_t) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
bool show_user_defined_docstrings = true; //< Include user-supplied texts in docstrings.
|
||||||
|
bool show_function_signatures = true; //< Include auto-generated function signatures in docstrings.
|
||||||
|
};
|
||||||
|
|
||||||
|
static state &global_state() {
|
||||||
|
static state instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
state previous_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
NAMESPACE_END(pybind11)
|
@ -34,6 +34,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "attr.h"
|
#include "attr.h"
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
NAMESPACE_BEGIN(pybind11)
|
NAMESPACE_BEGIN(pybind11)
|
||||||
|
|
||||||
@ -306,7 +307,7 @@ protected:
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
/* Create a nice pydoc rec including all signatures and
|
/* Create a nice pydoc rec including all signatures and
|
||||||
docstrings of the functions in the overload chain */
|
docstrings of the functions in the overload chain */
|
||||||
if (chain) {
|
if (chain && options::show_function_signatures()) {
|
||||||
// First a generic signature
|
// First a generic signature
|
||||||
signatures += rec->name;
|
signatures += rec->name;
|
||||||
signatures += "(*args, **kwargs)\n";
|
signatures += "(*args, **kwargs)\n";
|
||||||
@ -314,15 +315,17 @@ protected:
|
|||||||
}
|
}
|
||||||
// Then specific overload signatures
|
// Then specific overload signatures
|
||||||
for (auto it = chain_start; it != nullptr; it = it->next) {
|
for (auto it = chain_start; it != nullptr; it = it->next) {
|
||||||
if (chain)
|
if (options::show_function_signatures()) {
|
||||||
signatures += std::to_string(++index) + ". ";
|
if (chain)
|
||||||
signatures += rec->name;
|
signatures += std::to_string(++index) + ". ";
|
||||||
signatures += it->signature;
|
signatures += rec->name;
|
||||||
signatures += "\n";
|
signatures += it->signature;
|
||||||
if (it->doc && strlen(it->doc) > 0) {
|
|
||||||
signatures += "\n";
|
signatures += "\n";
|
||||||
|
}
|
||||||
|
if (it->doc && strlen(it->doc) > 0 && options::show_user_defined_docstrings()) {
|
||||||
|
if (options::show_function_signatures()) signatures += "\n";
|
||||||
signatures += it->doc;
|
signatures += it->doc;
|
||||||
signatures += "\n";
|
if (options::show_function_signatures()) signatures += "\n";
|
||||||
}
|
}
|
||||||
if (it->next)
|
if (it->next)
|
||||||
signatures += "\n";
|
signatures += "\n";
|
||||||
@ -532,6 +535,7 @@ public:
|
|||||||
PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check)
|
PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check)
|
||||||
|
|
||||||
explicit module(const char *name, const char *doc = nullptr) {
|
explicit module(const char *name, const char *doc = nullptr) {
|
||||||
|
if (!options::show_user_defined_docstrings()) doc = nullptr;
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
PyModuleDef *def = new PyModuleDef();
|
PyModuleDef *def = new PyModuleDef();
|
||||||
memset(def, 0, sizeof(PyModuleDef));
|
memset(def, 0, sizeof(PyModuleDef));
|
||||||
@ -562,7 +566,7 @@ public:
|
|||||||
std::string full_name = std::string(PyModule_GetName(m_ptr))
|
std::string full_name = std::string(PyModule_GetName(m_ptr))
|
||||||
+ std::string(".") + std::string(name);
|
+ std::string(".") + std::string(name);
|
||||||
module result(PyImport_AddModule(full_name.c_str()), true);
|
module result(PyImport_AddModule(full_name.c_str()), true);
|
||||||
if (doc)
|
if (doc && options::show_user_defined_docstrings())
|
||||||
result.attr("__doc__") = pybind11::str(doc);
|
result.attr("__doc__") = pybind11::str(doc);
|
||||||
attr(name) = result;
|
attr(name) = result;
|
||||||
return result;
|
return result;
|
||||||
@ -669,7 +673,7 @@ protected:
|
|||||||
: std::string(rec->name));
|
: std::string(rec->name));
|
||||||
|
|
||||||
char *tp_doc = nullptr;
|
char *tp_doc = nullptr;
|
||||||
if (rec->doc) {
|
if (rec->doc && options::show_user_defined_docstrings()) {
|
||||||
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
||||||
Python will free this later on) */
|
Python will free this later on) */
|
||||||
size_t size = strlen(rec->doc) + 1;
|
size_t size = strlen(rec->doc) + 1;
|
||||||
@ -1114,7 +1118,7 @@ public:
|
|||||||
rec_fset->doc = strdup(rec_fset->doc);
|
rec_fset->doc = strdup(rec_fset->doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pybind11::str doc_obj = pybind11::str(rec_fget->doc ? rec_fget->doc : "");
|
pybind11::str doc_obj = pybind11::str((rec_fget->doc && pybind11::options::show_user_defined_docstrings()) ? rec_fget->doc : "");
|
||||||
object property(
|
object property(
|
||||||
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
|
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
|
||||||
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
|
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
|
||||||
|
@ -14,6 +14,7 @@ set(PYBIND11_TEST_FILES
|
|||||||
test_class_args.cpp
|
test_class_args.cpp
|
||||||
test_constants_and_functions.cpp
|
test_constants_and_functions.cpp
|
||||||
test_copy_move_policies.cpp
|
test_copy_move_policies.cpp
|
||||||
|
test_docstring_options.cpp
|
||||||
test_eigen.cpp
|
test_eigen.cpp
|
||||||
test_enum.cpp
|
test_enum.cpp
|
||||||
test_eval.cpp
|
test_eval.cpp
|
||||||
|
53
tests/test_docstring_options.cpp
Normal file
53
tests/test_docstring_options.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
tests/test_docstring_options.cpp -- generation of docstrings and signatures
|
||||||
|
|
||||||
|
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
||||||
|
|
||||||
|
All rights reserved. Use of this source code is governed by a
|
||||||
|
BSD-style license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pybind11_tests.h"
|
||||||
|
|
||||||
|
struct DocstringTestFoo {
|
||||||
|
int value;
|
||||||
|
void setValue(int v) { value = v; }
|
||||||
|
int getValue() const { return value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
test_initializer docstring_generation([](py::module &m) {
|
||||||
|
|
||||||
|
{
|
||||||
|
py::options options;
|
||||||
|
options.disable_function_signatures();
|
||||||
|
|
||||||
|
m.def("test_function1", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||||
|
m.def("test_function2", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||||
|
|
||||||
|
options.enable_function_signatures();
|
||||||
|
|
||||||
|
m.def("test_function3", [](int, int) {}, py::arg("a"), py::arg("b"));
|
||||||
|
m.def("test_function4", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||||
|
|
||||||
|
options.disable_function_signatures().disable_user_defined_docstrings();
|
||||||
|
|
||||||
|
m.def("test_function5", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||||
|
|
||||||
|
{
|
||||||
|
py::options nested_options;
|
||||||
|
nested_options.enable_user_defined_docstrings();
|
||||||
|
m.def("test_function6", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.def("test_function7", [](int, int) {}, py::arg("a"), py::arg("b"), "A custom docstring");
|
||||||
|
|
||||||
|
{
|
||||||
|
py::options options;
|
||||||
|
options.disable_user_defined_docstrings();
|
||||||
|
|
||||||
|
py::class_<DocstringTestFoo>(m, "DocstringTestFoo", "This is a class docstring")
|
||||||
|
.def_property("value_prop", &DocstringTestFoo::getValue, &DocstringTestFoo::setValue, "This is a property docstring")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
});
|
32
tests/test_docstring_options.py
Normal file
32
tests/test_docstring_options.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from pybind11_tests import ConstructorStats
|
||||||
|
|
||||||
|
def test_docstring_options(capture):
|
||||||
|
from pybind11_tests import (test_function1, test_function2, test_function3,
|
||||||
|
test_function4, test_function5, test_function6,
|
||||||
|
test_function7, DocstringTestFoo)
|
||||||
|
|
||||||
|
# options.disable_function_signatures()
|
||||||
|
assert not test_function1.__doc__
|
||||||
|
|
||||||
|
assert test_function2.__doc__ == "A custom docstring"
|
||||||
|
|
||||||
|
# options.enable_function_signatures()
|
||||||
|
assert test_function3.__doc__ .startswith("test_function3(a: int, b: int) -> None")
|
||||||
|
|
||||||
|
assert test_function4.__doc__ .startswith("test_function4(a: int, b: int) -> None")
|
||||||
|
assert test_function4.__doc__ .endswith("A custom docstring\n")
|
||||||
|
|
||||||
|
# options.disable_function_signatures()
|
||||||
|
# options.disable_user_defined_docstrings()
|
||||||
|
assert not test_function5.__doc__
|
||||||
|
|
||||||
|
# nested options.enable_user_defined_docstrings()
|
||||||
|
assert test_function6.__doc__ == "A custom docstring"
|
||||||
|
|
||||||
|
# RAII destructor
|
||||||
|
assert test_function7.__doc__ .startswith("test_function7(a: int, b: int) -> None")
|
||||||
|
assert test_function7.__doc__ .endswith("A custom docstring\n")
|
||||||
|
|
||||||
|
# Suppression of user-defined docstrings for non-function objects
|
||||||
|
assert not DocstringTestFoo.__doc__
|
||||||
|
assert not DocstringTestFoo.value_prop.__doc__
|
Loading…
Reference in New Issue
Block a user