Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor directive addition #780

Merged
merged 3 commits into from
Jan 22, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -28,6 +28,9 @@ Inspired by `Keepachangelog.com <http://keepachangelog.com/>`__.
`#767 <https://github.com/michaeljones/breathe/issues/767>`__
- Doxygen >= 1.9.2 supports C++20 concepts, add support for them.
`#779 <https://github.com/michaeljones/breathe/pull/779>`__
- Change the way directives are added to adhere to the interface,
e.g., avoiding myst-parser to crash.
`#780 <https://github.com/michaeljones/breathe/pull/780>`__

- 2021-09-14 - **Breathe v4.31.0**

48 changes: 29 additions & 19 deletions breathe/directives/__init__.py
Original file line number Diff line number Diff line change
@@ -41,26 +41,36 @@ def format(self, text: str) -> str:


class BaseDirective(SphinxDirective):
required_arguments: int
optional_arguments: int
option_spec: Dict[str, Any]
has_content: bool
final_argument_whitespace: bool
@property
def directive_args(self) -> list:
# the order must be the same as in docutils.parsers.rst.Directive.__init__
return [
self.name,
self.arguments,
self.options,
self.content,
self.lineno,
self.content_offset,
self.block_text,
self.state,
self.state_machine,
]

def __init__(
self,
finder_factory: FinderFactory,
project_info_factory: ProjectInfoFactory,
parser_factory: DoxygenParserFactory,
*args
) -> None:
super().__init__(*args)
self.directive_args = list(args) # Convert tuple to list to allow modification.

self.finder_factory = finder_factory
self.project_info_factory = project_info_factory
self.filter_factory = FilterFactory(self.env.app)
self.parser_factory = parser_factory
@property
def project_info_factory(self) -> ProjectInfoFactory:
return self.env.temp_data["breathe_project_info_factory"]

@property
def parser_factory(self) -> DoxygenParserFactory:
return self.env.temp_data["breathe_parser_factory"]

@property
def finder_factory(self) -> FinderFactory:
return FinderFactory(self.env.app, self.parser_factory)

@property
def filter_factory(self) -> FilterFactory:
return FilterFactory(self.env.app)

@property
def kind(self) -> str:
53 changes: 14 additions & 39 deletions breathe/directives/setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from breathe.directives import BaseDirective
from breathe.directives.class_like import (
DoxygenStructDirective,
DoxygenClassDirective,
@@ -21,7 +20,6 @@
DoxygenEnumValueDirective,
DoxygenTypedefDirective,
)
from breathe.finder.factory import FinderFactory
from breathe.parser import DoxygenParserFactory
from breathe.project import ProjectInfoFactory
from breathe.process import AutoDoxygenProcessHandle
@@ -34,34 +32,6 @@
from typing import Any, List, Optional, Type # noqa


class DirectiveContainer:
def __init__(
self,
app: Sphinx,
directive: Type[BaseDirective],
finder_factory: FinderFactory,
project_info_factory: ProjectInfoFactory,
parser_factory: DoxygenParserFactory,
):
self.app = app
self.directive = directive
self.finder_factory = finder_factory
self.project_info_factory = project_info_factory
self.parser_factory = parser_factory

# Required for sphinx to inspect
self.required_arguments = directive.required_arguments
self.optional_arguments = directive.optional_arguments
self.option_spec = directive.option_spec
self.has_content = directive.has_content
self.final_argument_whitespace = directive.final_argument_whitespace

def __call__(self, *args):
call_args = [self.finder_factory, self.project_info_factory, self.parser_factory]
call_args.extend(args)
return self.directive(*call_args)


def setup(app: Sphinx) -> None:
directives = {
"doxygenindex": DoxygenIndexDirective,
@@ -84,21 +54,26 @@ def setup(app: Sphinx) -> None:
"doxygenpage": DoxygenPageDirective,
}

# The directives need these global objects, so in order to smuggle
# them in, we use env.temp_data. But it is cleared after each document
# has been read, we use the source-read event to set them.
# note: the parser factory contains a cache of the parsed XML
# note: the project_info_factory also contains some caching stuff
# TODO: is that actually safe for when reading in parallel?
project_info_factory = ProjectInfoFactory(app)
parser_factory = DoxygenParserFactory(app)
finder_factory = FinderFactory(app, parser_factory)

def set_temp_data(
app: Sphinx, project_info_factory=project_info_factory, parser_factory=parser_factory
):
assert app.env is not None
app.env.temp_data["breathe_project_info_factory"] = project_info_factory
app.env.temp_data["breathe_parser_factory"] = parser_factory

app.connect("source-read", lambda app, docname, source: set_temp_data(app))

for name, directive in directives.items():
# ordinarily app.add_directive takes a class it self, but we need to inject extra arguments
# so we give a DirectiveContainer object which has an overloaded __call__ operator.
app.add_directive(
name,
DirectiveContainer( # type: ignore
app, directive, finder_factory, project_info_factory, parser_factory
),
)
app.add_directive(name, directive)

app.add_config_value("breathe_projects", {}, True) # Dict[str, str]
app.add_config_value("breathe_default_project", "", True) # str
13 changes: 6 additions & 7 deletions tests/test_renderer.py
Original file line number Diff line number Diff line change
@@ -111,9 +111,14 @@ def __init__(self, **kwargs):

class MockState:
def __init__(self, app):
from breathe.project import ProjectInfoFactory
from breathe.parser import DoxygenParserFactory

env = sphinx.environment.BuildEnvironment(app)
env.setup(app)
env.temp_data["docname"] = "mock-doc"
env.temp_data["breathe_project_info_factory"] = ProjectInfoFactory(app)
env.temp_data["breathe_parser_factory"] = DoxygenParserFactory(app)
settings = frontend.OptionParser(components=(parsers.rst.Parser,)).get_default_values()
settings.env = env
self.document = utils.new_document("", settings)
@@ -516,19 +521,13 @@ def test_render_innergroup(app):

def get_directive(app):
from breathe.directives.function import DoxygenFunctionDirective
from breathe.project import ProjectInfoFactory
from breathe.parser import DoxygenParserFactory
from breathe.finder.factory import FinderFactory
from docutils.statemachine import StringList

app.config.breathe_separate_member_pages = False
app.config.breathe_default_project = "test_project"
app.config.breathe_domain_by_extension = {}
app.config.breathe_domain_by_file_pattern = {}
app.config.breathe_use_project_refids = False
project_info_factory = ProjectInfoFactory(app)
parser_factory = DoxygenParserFactory(app)
finder_factory = FinderFactory(app, parser_factory)
cls_args = (
"doxygenclass",
["at::Tensor"],
@@ -543,7 +542,7 @@ def get_directive(app):
MockState(app),
MockStateMachine(),
)
return DoxygenFunctionDirective(finder_factory, project_info_factory, parser_factory, *cls_args)
return DoxygenFunctionDirective(*cls_args)


def get_matches(datafile):