Skip to content

Commit

Permalink
Include docstring in doxygen output
Browse files Browse the repository at this point in the history
  • Loading branch information
jasujm committed Mar 14, 2020
1 parent 9baa817 commit 9cdbfe9
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 19 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
Unreleased
----------

Added
- Docstring from the Python enum definition is now included in the
generated Doxygen comments

Version 0.2
-----------

Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ wheel = "*"
jinja2 = ">=2.10"
regex = "*"
inflect = ">=3.0"
docstring-parser = ">=0.6"

[requires]
python_version = "3.7"
9 changes: 8 additions & 1 deletion Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions docs/enumecglib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ to give the generator a Python enum type.
The mapping between the name of the enum type, and the names and
values of the enum members are obvious in this style.

.. _enumecg-definition-from-dict:

Creating C++ enum from a dict
.............................

Expand Down Expand Up @@ -70,6 +72,9 @@ The supported keys are:
default, but to be more explicit, :class:`collections.OrderedDict` might
be preferred.

- ``docstring``: An optional documentation for the generated enum
definition. See :ref:`enumecg-documentation-generation` for details.

Native representation
.....................

Expand Down Expand Up @@ -167,6 +172,8 @@ error:
...
ValueError: Could not find common case

.. _enumecg-primary-enum:

Primary enum type
.................

Expand Down Expand Up @@ -339,8 +346,10 @@ object is used as argument, :func:`make_definition()` accepts all the
same options, that will be applied when creating the
:class:`EnumDefinition` object.

Including comments in the generator output
..........................................
.. _enumecg-documentation-generation:

Including documentation in the generator output
...............................................

Doxygen comments can be included by using the ``documentation``
option:
Expand All @@ -350,7 +359,10 @@ option:
>>> enumecg.generate(Status, documentation="doxygen")
'/** \\brief ...'

Currently "doxygen" is the only supported documentation style.
The generated documentation contains information about the usage of an
enhanced enum type. The doxygen documentation of
:ref:`enumecg-primary-enum` also includes the possible docstring of
the Python enum definition.

.. _enumecg-high-level-api:

Expand Down
6 changes: 3 additions & 3 deletions python/enumecg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ def generate(enum, **options) -> str:
- An instance of :class:`definitions.EnumDefinition`
- A ``dict`` containing "typename" and "members" keys, "members"
itself being a ``dict`` containing enumerator name to value
mapping.
- A ``dict`` object containing the enum definition. The required
and optional keys are discussed in
:ref:`enumecg-definition-from-dict`.
- A native Python :class:`enum.Enum` class. The typename is
derived from the name of the enum class, and the enumerator
Expand Down
26 changes: 26 additions & 0 deletions python/enumecg/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@
import dataclasses
import typing

import docstring_parser

from . import utils


@dataclasses.dataclass
class EnumDocumentation:
"""Documentation associated with an enum"""

short_description: typing.Optional[str]
long_description: typing.Optional[str]


@dataclasses.dataclass
class EnumMemberDefinition:
"""Enum member definition"""
Expand All @@ -33,6 +43,8 @@ class EnumDefinition:
value_type_typename: str
members: typing.Sequence[EnumMemberDefinition]
associate_namespace_name: str
label_enum_documentation: typing.Optional[EnumDocumentation] = None
enhanced_enum_documentation: typing.Optional[EnumDocumentation] = None


def _make_definition_from_dict(enum_dict, **options):
Expand All @@ -54,6 +66,15 @@ def _make_definition_from_dict(enum_dict, **options):
type_deducer = utils.CppTypeDeducer(
*members.values(), type_name=options.get("value_type")
)
unparsed_docstring = enum_dict.get("docstring")
if unparsed_docstring:
parsed_docstring = docstring_parser.parse(unparsed_docstring)
documentation = EnumDocumentation(
short_description=parsed_docstring.short_description,
long_description=parsed_docstring.long_description,
)
else:
documentation = None
return EnumDefinition(
label_enum_typename=label_enum_typename,
enhanced_enum_typename=enhanced_enum_typename,
Expand All @@ -71,6 +92,10 @@ def _make_definition_from_dict(enum_dict, **options):
for (n, (member_name, member_value)) in enumerate(members.items())
],
associate_namespace_name=formatter.join(formatter.parts[0], pluralize=True),
label_enum_documentation=documentation if primary_type == "label" else None,
enhanced_enum_documentation=documentation
if primary_type == "enhanced"
else None,
)


Expand All @@ -80,6 +105,7 @@ def _extract_python_enum_attrs(enum):
"members": collections.OrderedDict(
(member.name, member.value) for member in enum
),
"docstring": enum.__doc__,
}


Expand Down
5 changes: 5 additions & 0 deletions python/enumecg/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ def _make_initializer_list_ensure_outer_braces(value):
return _make_initializer_list(value)


def _doxygenize(value):
return value.replace("\n", "\n * ")


def _create_jinja_env():
env = jinja2.Environment(loader=jinja2.PackageLoader(__name__))
env.filters["initializer_list"] = _make_initializer_list_ensure_outer_braces
env.filters["doxygenize"] = _doxygenize
return env


Expand Down
11 changes: 6 additions & 5 deletions python/enumecg/templates/doxygen/enhanced_enum.doc.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** \brief Enhanced enum from label \ref {{ d.label_enum_typename }}
*
* This enum was autogenerated for the enhanced enum library. Please see the
* documentation for more information: https://enhanced-enum.readthedocs.io/
*/
{%- set doc = d.enhanced_enum_documentation -%}
{%- set default_doc -%}
Enhanced enum for \ref {{ d.label_enum_typename }}
{%- endset -%}
{%- set sa_doc = d.label_enum_typename -%}
{% include "doxygen/enum.doc.in" %}
17 changes: 17 additions & 0 deletions python/enumecg/templates/doxygen/enum.doc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/** \brief {% if doc.short_description -%}
{{ doc.short_description }}
{%- else -%}
{{ default_doc }}
{%- endif %}
{%- if doc.long_description %}
*
* {{ doc.long_description | doxygenize }}
{%- endif %}
*
* This enum was autogenerated for the enhanced enum library. Please see the
* documentation for more information: https://enhanced-enum.readthedocs.io/
{%- if doc.short_description %}
*
* \sa {{ sa_doc }}
{%- endif %}
*/
8 changes: 6 additions & 2 deletions python/enumecg/templates/doxygen/label_enum.doc.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/** \brief Label enum for \ref {{ d.enhanced_enum_typename }}
*/
{%- set doc = d.label_enum_documentation -%}
{%- set default_doc -%}
Label enum for \ref {{ d.enhanced_enum_typename }}
{%- endset -%}
{%- set sa_doc = d.enhanced_enum_typename -%}
{% include "doxygen/enum.doc.in" %}
2 changes: 1 addition & 1 deletion python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def get_package_dunder(name):
"Operating System :: OS Independent",
],
python_requires=">=3.7",
install_requires=["Jinja2>=2.10", "regex", "inflect>=3.0"],
install_requires=["Jinja2>=2.10", "regex", "inflect>=3.0", "docstring-parser>=0.6"],
)

# This is to bootstrap the build in case it is made with Enhanced Enum
Expand Down
13 changes: 12 additions & 1 deletion python/tests/common.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import enum

from enumecg.definitions import EnumDefinition, EnumMemberDefinition
from enumecg.definitions import EnumDefinition, EnumMemberDefinition, EnumDocumentation


class Status(enum.Enum):
"""An example enumeration for testing
This is a long description of the test enum."""

INITIALIZING = "initializing"
WAITING_FOR_INPUT = "waitingForInput"
BUSY = "busy"
Expand Down Expand Up @@ -35,6 +39,7 @@ class Status(enum.Enum):

STATUS_DEFINITION_DICT = {
"typename": "Status",
"docstring": Status.__doc__,
"members": {
"INITIALIZING": "initializing",
"WAITING_FOR_INPUT": "waitingForInput",
Expand All @@ -47,3 +52,9 @@ class Status(enum.Enum):
"typename": "NestedEnum",
"members": {"enumerator": (0, ("string", True))},
}


STATUS_DOCUMENTATION = EnumDocumentation(
short_description="An example enumeration for testing",
long_description="This is a long description of the test enum.",
)
3 changes: 3 additions & 0 deletions python/tests/test_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Status,
STATUS_DEFINITION_DICT,
NESTED_ENUM_DEFINITION_DICT,
STATUS_DOCUMENTATION,
)

from enumecg.definitions import EnumDefinition, EnumMemberDefinition, make_definition
Expand All @@ -27,13 +28,15 @@ def test_make_definition_should_raise_error_on_unknown_type(self):
def test_make_definition_with_label_type_as_primary(self):
definition = copy.copy(STATUS_DEFINITION)
definition.label_enum_typename = "Status"
definition.label_enum_documentation = STATUS_DOCUMENTATION
self.assertEqual(
make_definition(STATUS_DEFINITION_DICT, primary_type="label"), definition
)

def test_make_definition_with_enhanced_type_as_primary(self):
definition = copy.copy(STATUS_DEFINITION)
definition.enhanced_enum_typename = "Status"
definition.enhanced_enum_documentation = STATUS_DOCUMENTATION
self.assertEqual(
make_definition(STATUS_DEFINITION_DICT, primary_type="enhanced"), definition
)
Expand Down
39 changes: 36 additions & 3 deletions python/tests/test_generators.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import copy
import unittest

from enumecg.generators import CodeGenerator

from .common import STATUS_DEFINITION
from .common import STATUS_DEFINITION, STATUS_DOCUMENTATION


def _generate_enum_definitions(**options):
return CodeGenerator(**options).generate_enum_definitions(STATUS_DEFINITION)
def _generate_enum_definitions(definition=STATUS_DEFINITION, **options):
return CodeGenerator(**options).generate_enum_definitions(definition)


class CodeGeneratorTest(unittest.TestCase):
Expand Down Expand Up @@ -49,5 +50,37 @@ def test_enum_definitions_should_not_contain_documentation_if_not_requested(self
def test_enum_definitions_should_contain_documentation_if_requested(self):
self.assertIn("/**", _generate_enum_definitions(documentation="doxygen"))

def test_label_enum_documentation_should_contain_short_description(self):
definition = copy.copy(STATUS_DEFINITION)
definition.label_enum_documentation = STATUS_DOCUMENTATION
self.assertIn(
STATUS_DOCUMENTATION.short_description,
_generate_enum_definitions(definition, documentation="doxygen"),
)

def test_label_enum_documentation_should_contain_long_description(self):
definition = copy.copy(STATUS_DEFINITION)
definition.label_enum_documentation = STATUS_DOCUMENTATION
self.assertIn(
STATUS_DOCUMENTATION.long_description,
_generate_enum_definitions(definition, documentation="doxygen"),
)

def test_enhanced_enum_documentation_should_contain_short_description(self):
definition = copy.copy(STATUS_DEFINITION)
definition.enhanced_enum_documentation = STATUS_DOCUMENTATION
self.assertIn(
STATUS_DOCUMENTATION.short_description,
_generate_enum_definitions(definition, documentation="doxygen"),
)

def test_enhanced_enum_documentation_should_contain_long_description(self):
definition = copy.copy(STATUS_DEFINITION)
definition.enhanced_enum_documentation = STATUS_DOCUMENTATION
self.assertIn(
STATUS_DOCUMENTATION.long_description,
_generate_enum_definitions(definition, documentation="doxygen"),
)

def test_unsupported_documentation_style(self):
self.assertRaises(ValueError, CodeGenerator, documentation="invalid")

0 comments on commit 9cdbfe9

Please sign in to comment.