mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 06:35:12 +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/complex.h
|
||||
include/pybind11/descr.h
|
||||
include/pybind11/options.h
|
||||
include/pybind11/eigen.h
|
||||
include/pybind11/eval.h
|
||||
include/pybind11/functional.h
|
||||
|
@ -202,5 +202,28 @@ work, it is important that all lines are indented consistently, i.e.:
|
||||
----------
|
||||
)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
|
||||
.. [#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
|
||||
|
||||
#include "attr.h"
|
||||
#include "options.h"
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
@ -306,7 +307,7 @@ protected:
|
||||
int index = 0;
|
||||
/* Create a nice pydoc rec including all signatures and
|
||||
docstrings of the functions in the overload chain */
|
||||
if (chain) {
|
||||
if (chain && options::show_function_signatures()) {
|
||||
// First a generic signature
|
||||
signatures += rec->name;
|
||||
signatures += "(*args, **kwargs)\n";
|
||||
@ -314,15 +315,17 @@ protected:
|
||||
}
|
||||
// Then specific overload signatures
|
||||
for (auto it = chain_start; it != nullptr; it = it->next) {
|
||||
if (chain)
|
||||
signatures += std::to_string(++index) + ". ";
|
||||
signatures += rec->name;
|
||||
signatures += it->signature;
|
||||
signatures += "\n";
|
||||
if (it->doc && strlen(it->doc) > 0) {
|
||||
if (options::show_function_signatures()) {
|
||||
if (chain)
|
||||
signatures += std::to_string(++index) + ". ";
|
||||
signatures += rec->name;
|
||||
signatures += it->signature;
|
||||
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 += "\n";
|
||||
if (options::show_function_signatures()) signatures += "\n";
|
||||
}
|
||||
if (it->next)
|
||||
signatures += "\n";
|
||||
@ -532,6 +535,7 @@ public:
|
||||
PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check)
|
||||
|
||||
explicit module(const char *name, const char *doc = nullptr) {
|
||||
if (!options::show_user_defined_docstrings()) doc = nullptr;
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyModuleDef *def = new PyModuleDef();
|
||||
memset(def, 0, sizeof(PyModuleDef));
|
||||
@ -562,7 +566,7 @@ public:
|
||||
std::string full_name = std::string(PyModule_GetName(m_ptr))
|
||||
+ std::string(".") + std::string(name);
|
||||
module result(PyImport_AddModule(full_name.c_str()), true);
|
||||
if (doc)
|
||||
if (doc && options::show_user_defined_docstrings())
|
||||
result.attr("__doc__") = pybind11::str(doc);
|
||||
attr(name) = result;
|
||||
return result;
|
||||
@ -669,7 +673,7 @@ protected:
|
||||
: std::string(rec->name));
|
||||
|
||||
char *tp_doc = nullptr;
|
||||
if (rec->doc) {
|
||||
if (rec->doc && options::show_user_defined_docstrings()) {
|
||||
/* Allocate memory for docstring (using PyObject_MALLOC, since
|
||||
Python will free this later on) */
|
||||
size_t size = strlen(rec->doc) + 1;
|
||||
@ -1114,7 +1118,7 @@ public:
|
||||
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(
|
||||
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
|
||||
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_constants_and_functions.cpp
|
||||
test_copy_move_policies.cpp
|
||||
test_docstring_options.cpp
|
||||
test_eigen.cpp
|
||||
test_enum.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