From 230988bcf47447e1155522cd7f3d0285e2a20c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johann=20Kl=C3=A4hn?= Date: Wed, 2 Oct 2024 10:43:46 +0200 Subject: [PATCH] Add support for docstrings on enums Cf. https://github.com/pybind/pybind11/pull/1160 and https://github.com/pybind/pybind11/issues/2275#issuecomment-657264358 --- src/expose.cpp | 18 +++++++++++++---- tests/integration/docstrings.cpp | 1 + tests/integration/docstrings.h | 19 ++++++++++++++++++ tests/integration/docstrings_test.py | 20 +++++++++++++++++++ tests/integration/enums-can-be-arithmetic.h | 1 + .../enums-can-be-arithmetic_test.py | 4 ++++ 6 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/integration/docstrings.cpp create mode 100644 tests/integration/docstrings.h create mode 100644 tests/integration/docstrings_test.py diff --git a/src/expose.cpp b/src/expose.cpp index cd7a97b..9bf66d9 100644 --- a/src/expose.cpp +++ b/src/expose.cpp @@ -999,6 +999,10 @@ void EnumExposer::emitIntroducer(llvm::raw_ostream &os, emitType(os); os << "(" << parent_identifier << ", "; emitSpelling(os, enum_decl, annotations.lookup(enum_decl)); + if (llvm::StringRef doc = getBriefText(enum_decl); !doc.empty()) { + os << ", "; + emitStringLiteral(os, doc); + } if (const auto attrs = annotations.get(enum_decl); attrs.has_value() && attrs->arithmetic) { os << ", ::pybind11::arithmetic()"; @@ -1024,7 +1028,12 @@ void EnumExposer::handleDeclImpl(llvm::raw_ostream &os, const std::string scope = getFullyQualifiedName(enum_decl); os << "context.value("; emitSpelling(os, decl, annotations.lookup(decl)); - os << ", " << scope << "::" << enumerator->getName() << ");\n"; + os << ", " << scope << "::" << enumerator->getName(); + if (llvm::StringRef doc = getBriefText(decl); !doc.empty()) { + os << ", "; + emitStringLiteral(os, doc); + } + os << ");\n"; } } @@ -1050,6 +1059,10 @@ void RecordExposer::emitIntroducer(llvm::raw_ostream &os, os << "(" << parent_identifier << ", "; emitSpelling(os, record_decl, annotations.lookup(record_decl)); + if (llvm::StringRef doc = getBriefText(record_decl); !doc.empty()) { + os << ", "; + emitStringLiteral(os, doc); + } if (const auto attrs = annotations.get(record_decl); attrs.has_value() && attrs->dynamic_attr) { os << ", ::pybind11::dynamic_attr()"; @@ -1059,9 +1072,6 @@ void RecordExposer::emitIntroducer(llvm::raw_ostream &os, void RecordExposer::finalizeDefinition(llvm::raw_ostream &os) { emitProperties(os); - os << "context.doc() = "; - emitStringLiteral(os, getBriefText(record_decl)); - os << ";\n"; } void RecordExposer::emitProperties(llvm::raw_ostream &os) { diff --git a/tests/integration/docstrings.cpp b/tests/integration/docstrings.cpp new file mode 100644 index 0000000..66739af --- /dev/null +++ b/tests/integration/docstrings.cpp @@ -0,0 +1 @@ +#include "docstrings.h" diff --git a/tests/integration/docstrings.h b/tests/integration/docstrings.h new file mode 100644 index 0000000..5603665 --- /dev/null +++ b/tests/integration/docstrings.h @@ -0,0 +1,19 @@ +#pragma once + +#include "genpybind.h" + +/// Describes how the output will taste. +enum class GENPYBIND(visible) Flavor { + /// Like you would expect. + bland, + /// It tastes different. + fruity, +}; + +/// A contrived example. +/// +/// Only the “brief” docstring is used in the Python bindings. +class GENPYBIND(visible) Example {}; + +/// Also here. +class GENPYBIND(dynamic_attr) Dynamic {}; diff --git a/tests/integration/docstrings_test.py b/tests/integration/docstrings_test.py new file mode 100644 index 0000000..63db78a --- /dev/null +++ b/tests/integration/docstrings_test.py @@ -0,0 +1,20 @@ +import docstrings as m + + +def test_enums_support_docstrings(): + expected = """\ +Describes how the output will taste. + +Members: + + bland : Like you would expect. + + fruity : It tastes different.\ +""" + assert m.Flavor.__doc__ == expected + + +def test_classes_support_docstrings(): + assert m.Example.__doc__ == "A contrived example." + # Docstrings also work with extra arguments to `class_`. + assert m.Dynamic.__doc__ == "Also here." diff --git a/tests/integration/enums-can-be-arithmetic.h b/tests/integration/enums-can-be-arithmetic.h index 921bd17..7fffaf1 100644 --- a/tests/integration/enums-can-be-arithmetic.h +++ b/tests/integration/enums-can-be-arithmetic.h @@ -18,6 +18,7 @@ enum GENPYBIND(arithmetic(false)) ExplicitFalse { Three = 3, }; +/// Docstrings are also supported. enum GENPYBIND(arithmetic(true)) ExplicitTrue { Four = 4, Five = 5, diff --git a/tests/integration/enums-can-be-arithmetic_test.py b/tests/integration/enums-can-be-arithmetic_test.py index 97a25e2..fc86e61 100644 --- a/tests/integration/enums-can-be-arithmetic_test.py +++ b/tests/integration/enums-can-be-arithmetic_test.py @@ -24,3 +24,7 @@ def test_arithmetic_true_can_use_bitwise_operators(): def test_enums_arent_arithmetic_by_default(): with pytest.raises(TypeError, match="unsupported operand type"): m.Default.Seven | m.Default.Eight + + +def test_arithmetic_enums_support_docstrings(): + assert m.ExplicitTrue.__doc__.startswith("Docstrings are also supported.")