behavior is not enabled by default as it *might* have unwanted consequences in
pure Python code, enable it using the :py:`PYBIND11_COMPATIBILITY` option.
+`Enums`_
+--------
+
+Enums in pybind11 are not derived from :py:`enum.Enum`, but rather are plain
+classes. The only reliable way to detect a pybind11 enum is by looking for a
+``__members__`` member, which is a dict providing string names and their
+corresponding values. With pybind 2.2, it's only possible to document the
+enum class itself, not the values.
+
+.. note-info::
+
+ pybind 2.3 (not released yet) is scheduled to support docstrings for enum
+ values (see :gh:`pybind/pybind11#1160`). Support for this feature is not
+ done on the script side yet.
+
`Command-line options`_
=======================
:py:`enum.name` Enum name
:py:`enum.brief` Brief docs
:py:`enum.base` Base class from which the enum is
- derived
+ derived. Set to :py:`None` if no base
+ class information is available.
:py:`enum.values` List of enum values
:py:`enum.has_details` If there is enough content for the full
description block. [2]_
package-lock.json
test_doxygen/package-lock.json
test_python/*/output/
+test_python/build*
+test_python/*/*.so
# If nothing of the above matched, then it's a thing we want to document
return False
+def is_enum(state: State, object) -> bool:
+ return (inspect.isclass(object) and issubclass(object, enum.Enum)) or (state.config['PYBIND11_COMPATIBILITY'] and hasattr(object, '__members__'))
+
def make_url(path: List[str]) -> str:
return '.'.join(path) + '.html'
out.brief = extract_brief(class_.__doc__)
return out
-def extract_enum_doc(path: List[str], enum_):
- assert issubclass(enum_, enum.Enum)
-
+def extract_enum_doc(state: State, path: List[str], enum_):
out = Empty()
out.name = path[-1]
-
- # Enum doc is by default set to a generic value. That's useless as well.
- if enum_.__doc__ == 'An enumeration.':
- out.brief = ''
- else:
- out.brief = extract_brief(enum_.__doc__)
-
- out.base = extract_type(enum_.__base__)
out.values = []
out.has_details = False
out.has_value_details = False
- for i in enum_:
- value = Empty()
- value.name = i.name
- value.value = html.escape(repr(i.value))
-
- # Value doc gets by default inherited from the enum, that's useless
- if i.__doc__ == enum_.__doc__:
- value.brief = ''
+ # The happy case
+ if issubclass(enum_, enum.Enum):
+ # Enum doc is by default set to a generic value. That's useless as well.
+ if enum_.__doc__ == 'An enumeration.':
+ out.brief = ''
else:
- value.brief = extract_brief(i.__doc__)
+ out.brief = extract_brief(enum_.__doc__)
- if value.brief:
- out.has_details = True
- out.has_value_details = True
- out.values += [value]
+ out.base = extract_type(enum_.__base__)
+
+ for i in enum_:
+ value = Empty()
+ value.name = i.name
+ value.value = html.escape(repr(i.value))
+
+ # Value doc gets by default inherited from the enum, that's useless
+ if i.__doc__ == enum_.__doc__:
+ value.brief = ''
+ else:
+ value.brief = extract_brief(i.__doc__)
+
+ if value.brief:
+ out.has_details = True
+ out.has_value_details = True
+ out.values += [value]
+
+ # Pybind11 enums are ... different
+ elif state.config['PYBIND11_COMPATIBILITY']:
+ assert hasattr(enum_, '__members__')
+
+ out.brief = extract_brief(enum_.__doc__)
+ out.base = None
+
+ for name, v in enum_.__members__.items():
+ value = Empty()
+ value. name = name
+ value.value = int(v)
+ # TODO: once https://github.com/pybind/pybind11/pull/1160 is
+ # released, extract from class docs (until then the class
+ # docstring is duplicated here, which is useless)
+ value.brief = ''
+ out.values += [value]
return out
if inspect.ismodule(object):
page.modules += [extract_module_doc(subpath, object)]
index_entry.children += [render_module(state, subpath, object, env)]
- elif inspect.isclass(object) and not issubclass(object, enum.Enum):
+ elif inspect.isclass(object) and not is_enum(state, object):
page.classes += [extract_class_doc(subpath, object)]
index_entry.children += [render_class(state, subpath, object, env)]
- elif inspect.isclass(object) and issubclass(object, enum.Enum):
- enum_ = extract_enum_doc(subpath, object)
+ elif inspect.isclass(object) and is_enum(state, object):
+ enum_ = extract_enum_doc(state, subpath, object)
page.enums += [enum_]
if enum_.has_details: page.has_enum_details = True
elif inspect.isfunction(object) or inspect.isbuiltin(object):
index_entry.children += [render_module(state, subpath, object, env)]
# Get (and render) inner classes
- for name, object in inspect.getmembers(module, lambda o: inspect.isclass(o) and not issubclass(o, enum.Enum)):
+ for name, object in inspect.getmembers(module, lambda o: inspect.isclass(o) and not is_enum(state, o)):
if is_internal_or_imported_module_member(module, path, name, object): continue
subpath = path + [name]
index_entry.children += [render_class(state, subpath, object, env)]
# Get enums
- for name, object in inspect.getmembers(module, lambda o: inspect.isclass(o) and issubclass(o, enum.Enum)):
+ for name, object in inspect.getmembers(module, lambda o: is_enum(state, o)):
if is_internal_or_imported_module_member(module, path, name, object): continue
subpath = path + [name]
if not object.__doc__: logging.warning("%s is undocumented", '.'.join(subpath))
- enum_ = extract_enum_doc(subpath, object)
+ enum_ = extract_enum_doc(state, subpath, object)
page.enums += [enum_]
if enum_.has_details: page.has_enum_details = True
index_entry.brief = page.brief
# Get inner classes
- for name, object in inspect.getmembers(class_, lambda o: inspect.isclass(o) and not issubclass(o, enum.Enum)):
+ for name, object in inspect.getmembers(class_, lambda o: inspect.isclass(o) and not is_enum(state, o)):
if name in ['__base__', '__class__']: continue # TODO
if name.startswith('_'): continue
index_entry.children += [render_class(state, subpath, object, env)]
# Get enums
- for name, object in inspect.getmembers(class_, lambda o: inspect.isclass(o) and issubclass(o, enum.Enum)):
+ for name, object in inspect.getmembers(class_, lambda o: is_enum(state, o)):
if name.startswith('_'): continue
subpath = path + [name]
if not object.__doc__: logging.warning("%s is undocumented", '.'.join(subpath))
- enum_ = extract_enum_doc(subpath, object)
+ enum_ = extract_enum_doc(state, subpath, object)
page.enums += [enum_]
if enum_.has_details: page.has_enum_details = True
<dt>
{% set j = joiner('\n ') %}
- <span class="m-doc-wrap-bumper">class <a href="" class="m-doc{% if not enum.has_details %}-self{% endif %}">{{ enum.name }}</a>({{ enum.base }}): </span><span class="m-doc-wrap">{% for value in enum.values %}{{ j() }}<a href="" class="m-doc{% if not enum.has_details %}-self{% endif %}">{{ value.name }}</a>{% if value.value is not none %} = {{ value.value }}{% endif %}{% endfor %}</span>
+ <span class="m-doc-wrap-bumper">class <a href="" class="m-doc{% if not enum.has_details %}-self{% endif %}">{{ enum.name }}</a>{% if enum.base %}({{ enum.base }}){% endif %}: </span><span class="m-doc-wrap">{% for value in enum.values %}{{ j() }}<a href="" class="m-doc{% if not enum.has_details %}-self{% endif %}">{{ value.name }}</a>{% if value.value is not none %} = {{ value.value }}{% endif %}{% endfor %}</span>
</dt>
<dd>{{ enum.brief }}</dd>
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+cmake_minimum_required(VERSION 3.5)
+project(McssDocumentationPybindTests)
+
+find_package(pybind11 CONFIG REQUIRED)
+
+foreach(target pybind_enums)
+ pybind11_add_module(${target} ${target}/${target}.cpp)
+ set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${target})
+endforeach()
--- /dev/null
+#include <pybind11/pybind11.h>
+
+namespace py = pybind11;
+
+enum MyEnum {
+ First = 0,
+ Second = 1,
+ Third = 74,
+ Consistent = -5
+};
+
+enum SixtyfourBitFlag: std::uint64_t {
+ Yes = 1000000000000ull,
+ No = 18446744073709551615ull
+};
+
+PYBIND11_MODULE(pybind_enums, m) {
+ m.doc() = "pybind11 enum parsing";
+
+ py::enum_<MyEnum>(m, "MyEnum", "An enum without value docs :(")
+ .value("First", MyEnum::First)
+ .value("Second", MyEnum::Second)
+ .value("Third", MyEnum::Third)
+ .value("CONSISTANTE", MyEnum::Consistent);
+
+ py::enum_<SixtyfourBitFlag>(m, "SixtyfourBitFlag", "64-bit flags")
+ .value("Yes", SixtyfourBitFlag::Yes)
+ .value("No", SixtyfourBitFlag::No);
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>pybind_enums | My Python Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+documentation.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Python Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ pybind_enums <span class="m-thin">module</span>
+ </h1>
+ <p>pybind11 enum parsing</p>
+ <section id="enums">
+ <h2><a href="#enums">Enums</a></h2>
+ <dl class="m-doc">
+ <dt>
+ <span class="m-doc-wrap-bumper">class <a href="" class="m-doc-self">MyEnum</a>: </span><span class="m-doc-wrap"><a href="" class="m-doc-self">First</a> = 0
+ <a href="" class="m-doc-self">Second</a> = 1
+ <a href="" class="m-doc-self">Third</a> = 74
+ <a href="" class="m-doc-self">CONSISTANTE</a> = -5</span>
+ </dt>
+ <dd>An enum without value docs :(</dd>
+ <dt>
+ <span class="m-doc-wrap-bumper">class <a href="" class="m-doc-self">SixtyfourBitFlag</a>: </span><span class="m-doc-wrap"><a href="" class="m-doc-self">Yes</a> = 1000000000000
+ <a href="" class="m-doc-self">No</a> = 18446744073709551615</span>
+ </dt>
+ <dd>64-bit flags</dd>
+ </dl>
+ </section>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+#
+# This file is part of m.css.
+#
+# Copyright © 2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+from . import BaseTestCase
+
+class Enums(BaseTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, 'enums', *args, **kwargs)
+
+ def test(self):
+ self.run_python({
+ 'PYBIND11_COMPATIBILITY': True
+ })
+ self.assertEqual(*self.actual_expected_contents('pybind_enums.html'))