Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-breathe for openSUSE:Factory 
checked in at 2021-03-02 12:25:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-breathe (Old)
 and      /work/SRC/openSUSE:Factory/.python-breathe.new.2378 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-breathe"

Tue Mar  2 12:25:34 2021 rev:12 rq:874949 version:4.27.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-breathe/python-breathe.changes    
2020-12-12 20:32:09.513839157 +0100
+++ /work/SRC/openSUSE:Factory/.python-breathe.new.2378/python-breathe.changes  
2021-03-02 12:25:36.151324396 +0100
@@ -1,0 +2,19 @@
+Thu Feb 25 03:13:38 UTC 2021 - Steve Kowalik <steven.kowa...@suse.com>
+
+- Update to 4.27.0:
+  * Add various specifiers to functions and variables. #628
+  * Add multiply inherited class for PHP objects. #630
+  * Initial support for table rendering. #632
+  * Add rendering of \section, \subsection and \subsubsection. #635
+  * Sphinx 3.5 compatibility. #640
+  * Fix linking to sections. #639
+  * Add table examples to documentation. #638
+  * Fix doxygenfile causing duplicate IDs for unspecified sections. #622
+  * Fixes for doxygenfunction (friend keyword, friend class, arg checks). #623
+  * Add test for ellipsis ('...') in args. #610
+  * Sphinx 3.4.x compatibility. #617
+  * Adapt friendclass to Doxygen 1.9. #618
+  * Add support for \parblock parsing and rendering. #603
+  * Allow lookup in doxygenfunction without writing param names. #606 
+
+-------------------------------------------------------------------

Old:
----
  v4.24.1.tar.gz

New:
----
  v4.27.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-breathe.spec ++++++
--- /var/tmp/diff_new_pack.sTBIjm/_old  2021-03-02 12:25:36.823325028 +0100
+++ /var/tmp/diff_new_pack.sTBIjm/_new  2021-03-02 12:25:36.827325032 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-breathe
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %define skip_python2 1
 Name:           python-breathe
-Version:        4.24.1
+Version:        4.27.0
 Release:        0
 Summary:        Sphinx Doxygen renderer
 License:        BSD-3-Clause

++++++ v4.24.1.tar.gz -> v4.27.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/.github/workflows/unit_tests.yml 
new/breathe-4.27.0/.github/workflows/unit_tests.yml
--- old/breathe-4.24.1/.github/workflows/unit_tests.yml 2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/.github/workflows/unit_tests.yml 2021-02-16 
16:26:15.000000000 +0100
@@ -6,12 +6,15 @@
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.5, 3.6, 3.7, 3.8]
+        python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
         sphinx-version:
           - 3.0.4
           - 3.1.2
-          - 3.2.0
-          - git+https://github.com/sphinx-doc/sphinx.git@3.3.x
+          - 3.2.1
+          - 3.3.1
+          - 3.4.3
+          - 3.5.0
+          - git+https://github.com/sphinx-doc/sphinx.git@3.5.x
           - git+https://github.com/sphinx-doc/sphinx.git@3.x
           # master (Sphinx 4) will require at least Python 3.6, so disable it 
for now
           #- git+https://github.com/sphinx-doc/sphinx.git@master
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/README.rst 
new/breathe-4.27.0/README.rst
--- old/breathe-4.24.1/README.rst       2020-12-01 22:19:50.000000000 +0100
+++ new/breathe-4.27.0/README.rst       2021-02-16 16:26:15.000000000 +0100
@@ -172,6 +172,36 @@
 
 Inspired by `Keepachangelog.com <http://keepachangelog.com/>`__.
 
+- 2021-02-16 - Breathe v4.27.0
+
+  - Add various specifiers to functions and variables. #628
+  - Add multiply inherited class for PHP objects. #630
+  - Initial support for table rendering. #632
+  - Add rendering of \section, \subsection and \subsubsection. #635
+  - Sphinx 3.5 compatibility. #640
+  - Fix linking to sections. #639
+  - Add table examples to documentation. #638
+
+- 2021-01-21 - Breathe v4.26.1
+
+  - Fix doxygenfile causing duplicate IDs for unspecified sections. #622
+  - Fixes for doxygenfunction (friend keyword, friend class, arg checks). #623
+
+- 2021-01-08 - Breathe v4.26.0
+
+  - Add test for ellipsis ('...') in args. #610
+  - Sphinx 3.4.x compatibility. #617
+  - Adapt friendclass to Doxygen 1.9. #618
+
+- 2020-12-16 - Breathe v4.25.1
+
+  - Addendum to #606, for functions with '...'. #609
+
+- 2020-12-15 - Breathe v4.25.0
+
+  - Add support for \parblock parsing and rendering. #603
+  - Allow lookup in doxygenfunction without writing param names. #606
+
 - 2020-12-01 - Breathe v4.24.1
 
   - Fix anchors on pages generated by Doxygen >= 1.8.17. #602
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/__init__.py 
new/breathe-4.27.0/breathe/__init__.py
--- old/breathe-4.24.1/breathe/__init__.py      2020-12-01 22:19:50.000000000 
+0100
+++ new/breathe-4.27.0/breathe/__init__.py      2021-02-16 16:26:15.000000000 
+0100
@@ -4,7 +4,7 @@
 
 from sphinx.application import Sphinx
 
-__version__ = '4.24.1'
+__version__ = '4.27.0'
 
 
 def setup(app: Sphinx):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/directives.py 
new/breathe-4.27.0/breathe/directives.py
--- old/breathe-4.24.1/breathe/directives.py    2020-12-01 22:19:50.000000000 
+0100
+++ new/breathe-4.27.0/breathe/directives.py    2021-02-16 16:26:15.000000000 
+0100
@@ -28,7 +28,7 @@
 import re
 import subprocess
 
-from typing import Any, List, Type  # noqa
+from typing import Any, List, Optional, Type  # noqa
 
 
 class NoMatchingFunctionError(BreatheError):
@@ -76,13 +76,22 @@
             return warning.warn('doxygenfunction: %s' % e)
 
         # Extract arguments from the function name.
-        args = self.parse_args(args)
+        args = self._parse_args(args)
 
-        finder_filter = 
self.filter_factory.create_function_finder_filter(namespace, function_name)
+        finder_filter = 
self.filter_factory.create_function_and_all_friend_finder_filter(
+            namespace, function_name)
 
         # TODO: find a more specific type for the Doxygen nodes
-        matches = []  # type: List[Any]
-        finder.filter_(finder_filter, matches)
+        matchesAll = []  # type: List[Any]
+        finder.filter_(finder_filter, matchesAll)
+        matches = []
+        for m in matchesAll:
+            # only take functions and friend functions
+            # ignore friend classes
+            node = m[0]
+            if node.kind == 'friend' and not node.argsstring:
+                continue
+            matches.append(m)
 
         # Create it ahead of time as it is cheap and it is ugly to declare it 
for both exception
         # clauses below
@@ -92,43 +101,30 @@
             self.lineno,
             namespace='%s::' % namespace if namespace else '',
             function=function_name,
-            args=', '.join(args)
+            args=str(args)
             )
 
         try:
-            node_stack = self.resolve_function(matches, args, project_info)
+            node_stack = self._resolve_function(matches, args, project_info)
         except NoMatchingFunctionError:
             return warning.warn('doxygenfunction: Cannot find function 
"{namespace}{function}" '
                                 '{tail}')
         except UnableToResolveFunctionError as error:
-            message = 'doxygenfunction: Unable to resolve multiple matches for 
function ' \
-                '"{namespace}{function}" with arguments ({args}) {tail}.\n' \
+            message = 'doxygenfunction: Unable to resolve function ' \
+                '"{namespace}{function}" with arguments {args} {tail}.\n' \
                 'Potential matches:\n'
 
-            # We want to create a string for the console output and a set of 
docutils nodes
-            # for rendering into the final output. We handle the final output 
as a literal string
-            # with a txt based list of the options.
-            literal_text = ''
-
-            # TODO: We're cheating here with the set() as signatures has 
repeating entries for some
-            # reason (failures in the matcher_stack code) so we consolidate 
them by shoving them in
-            # a set to remove duplicates. Should be fixed!
-            signatures = ''
-            for i, entry in enumerate(sorted(set(error.signatures))):
-                if i:
-                    literal_text += '\n'
-                # Replace new lines with a new line & enough spacing to reach 
the appropriate
-                # alignment for our simple plain text list
-                literal_text += '- %s' % entry.replace('\n', '\n  ')
-                signatures += '    - %s\n' % entry.replace('\n', '\n      ')
-            block = nodes.literal_block('', '', nodes.Text(literal_text))
+            text = ''
+            for i, entry in enumerate(sorted(error.signatures)):
+                text += '- %s\n' % entry
+            block = nodes.literal_block('', '', nodes.Text(text))
             formatted_message = warning.format(message)
             warning_nodes = [
                 nodes.paragraph("", "", nodes.Text(formatted_message)),
                 block
             ]
             result = warning.warn(message, rendered_nodes=warning_nodes,
-                                  unformatted_suffix=signatures)
+                                  unformatted_suffix=text)
             return result
 
         target_handler = create_target_handler(self.options, project_info, 
self.state.document)
@@ -137,44 +133,29 @@
         return self.render(node_stack, project_info, filter_, target_handler, 
NullMaskFactory(),
                            self.directive_args)
 
-    def parse_args(self, function_description):
-        # Strip off trailing qualifiers
-        pattern = re.compile(r'''(?<= \)) \s*
-                             (?: = \s* 0)? \s* $ ''',
-                             re.VERBOSE)
-
-        function_description = re.sub(pattern,
-                                      '',
-                                      function_description)
-
-        paren_index = function_description.find('(')
-        if paren_index == -1:
-            return []
-        # If it is empty parenthesis, then return empty list as we want empty 
parenthesis coming
-        # from the xml file to match the user's function when the user doesn't 
provide parenthesis
-        # ie. when there are no args anyway
-        elif function_description == '()':
-            return []
-        else:
-            # Parse the function name string, eg. f(int, float) to
-            # extract the types so we can use them for matching
-            args = []
-            num_open_brackets = -1
-            start = paren_index + 1
-            for i in range(paren_index, len(function_description)):
-                c = function_description[i]
-                if c == '(' or c == '<':
-                    num_open_brackets += 1
-                elif c == ')' or c == '>':
-                    num_open_brackets -= 1
-                elif c == ',' and num_open_brackets == 0:
-                    args.append(function_description[start:i].strip())
-                    start = i + 1
-            args.append(function_description[start:-1].strip())
-            return args
+    def _parse_args(self, function_description: str) -> 
Optional[cpp.ASTParametersQualifiers]:
+        if function_description == '':
+            return None
+
+        parser = cpp.DefinitionParser(function_description,
+                                      location=self.get_source_info(),
+                                      config=self.config)
+        paramQual = 
parser._parse_parameters_and_qualifiers(paramMode='function')
+        # now erase the parameter names
+        for p in paramQual.args:
+            if p.arg is None:
+                assert p.ellipsis
+                continue
+            declarator = p.arg.type.decl
+            while hasattr(declarator, 'next'):
+                declarator = declarator.next  # type: ignore
+            assert hasattr(declarator, 'declId')
+            declarator.declId = None  # type: ignore
+            p.arg.init = None  # type: ignore
+        return paramQual
 
-    def create_function_signature(self, node_stack, project_info, filter_, 
target_handler,
-                                  mask_factory, directive_args):
+    def _create_function_signature(self, node_stack, project_info, filter_, 
target_handler,
+                                   mask_factory, directive_args) -> str:
         "Standard render process used by subclasses"
 
         try:
@@ -192,7 +173,8 @@
             return format_parser_error("doxygenclass", e.error, e.filename, 
self.state,
                                        self.lineno, True)
         except FileIOError as e:
-            return format_parser_error("doxygenclass", e.error, e.filename, 
self.state, self.lineno)
+            return format_parser_error("doxygenclass", e.error, e.filename, 
self.state,
+                                       self.lineno, False)
 
         context = RenderContext(node_stack, mask_factory, directive_args)
         node = node_stack[0]
@@ -212,16 +194,12 @@
         ast = parser.parse_declaration('function', 'function')
         return str(ast)
 
-    def resolve_function(self, matches, args, project_info):
+    def _resolve_function(self, matches, args: 
Optional[cpp.ASTParametersQualifiers], project_info):
         if not matches:
             raise NoMatchingFunctionError()
 
-        if len(matches) == 1:
-            return matches[0]
-
-        signatures = []
-
-        # Iterate over the potential matches
+        res = []
+        candSignatures = []
         for entry in matches:
             text_options = {'no-link': u'', 'outline': u''}
 
@@ -235,20 +213,29 @@
             directive_args = self.directive_args[:]
             directive_args[2] = text_options
 
-            signature = self.create_function_signature(entry, project_info, 
filter_, target_handler,
-                                                       mask_factory, 
directive_args)
-            signatures.append(signature)
-
-            match = re.match(r"([^(]*)(.*)", signature)
-            match_args = match.group(2)
-
-            # Parse the text to find the arguments
-            match_args = self.parse_args(match_args)
-
-            # Match them against the arg spec
-            if args == match_args:
-                return entry
-        raise UnableToResolveFunctionError(signatures)
+            signature = self._create_function_signature(entry, project_info, 
filter_,
+                                                        target_handler,
+                                                        mask_factory, 
directive_args)
+            candSignatures.append(signature)
+
+            if args is not None:
+                match = re.match(r"([^(]*)(.*)", signature)
+                assert match
+                _match_args = match.group(2)
+
+                # Parse the text to find the arguments
+                match_args = self._parse_args(_match_args)
+
+                # Match them against the arg spec
+                if args != match_args:
+                    continue
+
+            res.append((entry, signature))
+
+        if len(res) == 1:
+            return res[0][0]
+        else:
+            raise UnableToResolveFunctionError(candSignatures)
 
 
 class _DoxygenClassLikeDirective(BaseDirective):
@@ -603,7 +590,7 @@
     app.add_config_value("breathe_separate_member_pages", False, 'env')
 
     breathe_css = "breathe.css"
-    if (os.path.exists(os.path.join(app.confdir, "_static", breathe_css))):
+    if (os.path.exists(os.path.join(app.confdir, "_static", breathe_css))):  # 
type: ignore
         app.add_stylesheet(breathe_css)
 
     def write_file(directory, filename, content):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/parser/compound.py 
new/breathe-4.27.0/breathe/parser/compound.py
--- old/breathe-4.24.1/breathe/parser/compound.py       2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/breathe/parser/compound.py       2021-02-16 
16:26:15.000000000 +0100
@@ -1050,12 +1050,35 @@
             obj_ = supermod.docAnchorType.factory()
             obj_.build(child_)
             self.content.append(obj_)
+        elif child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "parblock":
+            obj_ = supermod.docParBlockType.factory()
+            obj_.build(child_)
+            self.content.append(obj_)
+        elif child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "table":
+            obj_ = supermod.docTableType.factory()
+            obj_.build(child_)
+            self.content.append(obj_)
 
 
 supermod.docParaType.subclass = docParaTypeSub
 # end class docParaTypeSub
 
 
+class docParBlockTypeSub(supermod.docParBlockType):
+
+    node_type = "docparblock"
+
+    def __init__(self, para=None):
+        supermod.docParBlockType.__init__(self, para)
+
+    def buildChildren(self, child_, nodeName_):
+        supermod.docParBlockType.buildChildren(self, child_, nodeName_)
+
+
+supermod.docParBlockType.subclass = docParBlockTypeSub
+# end class docParBlockTypeSub
+
+
 class docMarkupTypeSub(supermod.docMarkupType):
 
     node_type = "docmarkup"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/parser/compoundsuper.py 
new/breathe-4.27.0/breathe/parser/compoundsuper.py
--- old/breathe-4.24.1/breathe/parser/compoundsuper.py  2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/breathe/parser/compoundsuper.py  2021-02-16 
16:26:15.000000000 +0100
@@ -2888,6 +2888,10 @@
             self.content_ = []
         else:
             self.content_ = content_
+        if title is None:
+            self.title = ""
+        else:
+            self.title = title
     def factory(*args_, **kwargs_):
         if docSect1Type.subclass:
             return docSect1Type.subclass(*args_, **kwargs_)
@@ -2943,11 +2947,7 @@
     def buildChildren(self, child_, nodeName_):
         if child_.nodeType == Node.ELEMENT_NODE and \
             nodeName_ == 'title':
-            childobj_ = docTitleType.factory()
-            childobj_.build(child_)
-            obj_ = self.mixedclass_(MixedContainer.CategoryComplex,
-                MixedContainer.TypeNone, 'title', childobj_)
-            self.content_.append(obj_)
+            self.title = child_.childNodes[0].nodeValue
         elif child_.nodeType == Node.ELEMENT_NODE and \
             nodeName_ == 'para':
             childobj_ = docParaType.factory()
@@ -2989,6 +2989,10 @@
             self.content_ = []
         else:
             self.content_ = content_
+        if title is None:
+            title = ""
+        else:
+            title = title
     def factory(*args_, **kwargs_):
         if docSect2Type.subclass:
             return docSect2Type.subclass(*args_, **kwargs_)
@@ -3044,11 +3048,7 @@
     def buildChildren(self, child_, nodeName_):
         if child_.nodeType == Node.ELEMENT_NODE and \
             nodeName_ == 'title':
-            childobj_ = docTitleType.factory()
-            childobj_.build(child_)
-            obj_ = self.mixedclass_(MixedContainer.CategoryComplex,
-                MixedContainer.TypeNone, 'title', childobj_)
-            self.content_.append(obj_)
+            self.title = child_.childNodes[0].nodeValue
         elif child_.nodeType == Node.ELEMENT_NODE and \
             nodeName_ == 'para':
             childobj_ = docParaType.factory()
@@ -3090,6 +3090,10 @@
             self.content_ = []
         else:
             self.content_ = content_
+        if title is None:
+            self.title = ""
+        else:
+            self.title = title
     def factory(*args_, **kwargs_):
         if docSect3Type.subclass:
             return docSect3Type.subclass(*args_, **kwargs_)
@@ -3145,11 +3149,7 @@
     def buildChildren(self, child_, nodeName_):
         if child_.nodeType == Node.ELEMENT_NODE and \
             nodeName_ == 'title':
-            childobj_ = docTitleType.factory()
-            childobj_.build(child_)
-            obj_ = self.mixedclass_(MixedContainer.CategoryComplex,
-                MixedContainer.TypeNone, 'title', childobj_)
-            self.content_.append(obj_)
+            self.title = child_.childNodes[0].nodeValue
         elif child_.nodeType == Node.ELEMENT_NODE and \
             nodeName_ == 'para':
             childobj_ = docParaType.factory()
@@ -5682,6 +5682,72 @@
 # end class docCharType
 
 
+class docParBlockType(GeneratedsSuper):
+    subclass = None
+    superclass = None
+    def __init__(self, mixedclass_=None, para=None):
+        if mixedclass_ is None:
+            self.mixedclass_ = MixedContainer
+        else:
+            self.mixedclass_ = mixedclass_
+        if para is None:
+            self.para = []
+        else:
+            self.para = para
+    def factory(*args_, **kwargs_):
+        if docParBlockType.subclass:
+            return docParBlockType.subclass(*args_, **kwargs_)
+        else:
+            return docParBlockType(*args_, **kwargs_)
+    factory = staticmethod(factory)
+    def get_para(self): return self.para
+    def set_para(self, para): self.para = para
+    def add_para(self, value): self.para.append(value)
+    def insert_para(self, index, value): self.para[index] = value
+    def export(self, outfile, level, namespace_='', name_='docParBlockType', 
namespacedef_=''):
+        showIndent(outfile, level)
+        outfile.write('<%s%s%s' % (namespace_, name_, namespacedef_ and ' ' + 
namespacedef_ or '', ))
+        self.exportAttributes(outfile, level, namespace_, 
name_='docParBlockType')
+        if self.hasContent_():
+            outfile.write('>\n')
+            self.exportChildren(outfile, level + 1, namespace_, name_)
+            showIndent(outfile, level)
+            outfile.write('</%s%s>\n' % (namespace_, name_))
+        else:
+            outfile.write('/>\n')
+    def exportAttributes(self, outfile, level, namespace_='', 
name_='docParBlockType'):
+        pass
+    def exportChildren(self, outfile, level, namespace_='', 
name_='docParBlockType'):
+        for para_ in self.para:
+            para_.export(outfile, level, namespace_, name_='para')
+    def hasContent_(self):
+        if (
+            self.para
+            ):
+            return True
+        else:
+            return False
+    def build(self, node_):
+        attrs = node_.attributes
+        self.buildAttributes(attrs)
+        self.valueOf_ = ''
+        for child_ in node_.childNodes:
+            nodeName_ = child_.nodeName.split(':')[-1]
+            self.buildChildren(child_, nodeName_)
+    def buildAttributes(self, attrs):
+        pass
+    def buildChildren(self, child_, nodeName_):
+        if child_.nodeType == Node.ELEMENT_NODE and \
+            nodeName_ == 'para':
+            obj_ = docParaType.factory()
+            obj_.build(child_)
+            obj_ = self.mixedclass_(MixedContainer.CategoryComplex,
+                MixedContainer.TypeNone, 'para', obj_)
+            self.para.append(obj_)
+
+# end class docParBlockType
+
+
 class docEmptyType(GeneratedsSuper):
     subclass = None
     superclass = None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/path_handler.py 
new/breathe-4.27.0/breathe/path_handler.py
--- old/breathe-4.24.1/breathe/path_handler.py  2020-12-01 22:19:50.000000000 
+0100
+++ new/breathe-4.27.0/breathe/path_handler.py  2021-02-16 16:26:15.000000000 
+0100
@@ -15,4 +15,4 @@
     """
 
     # os.path.join does the appropriate handling if _project_path is an 
absolute path
-    return os.path.join(app.confdir, directory, filename)
+    return os.path.join(app.confdir, directory, filename)  # type: ignore
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/renderer/filter.py 
new/breathe-4.27.0/breathe/renderer/filter.py
--- old/breathe-4.24.1/breathe/renderer/filter.py       2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/breathe/renderer/filter.py       2021-02-16 
16:26:15.000000000 +0100
@@ -1026,16 +1026,17 @@
             return (parent_is_compound & parent_is_file & node_matches) \
                 | (parent_is_compound & parent_is_not_file & node_matches)
 
-    def create_function_finder_filter(self, namespace: str, name: str) -> 
Filter:
+    def create_function_and_all_friend_finder_filter(self, namespace: str, 
name: str) -> Filter:
         parent = Parent()
         parent_is_compound = parent.node_type == 'compound'
         parent_is_group = parent.kind == 'group'
 
         function_filter = self.create_member_finder_filter(namespace, name, 
'function')
+        friend_filter = self.create_member_finder_filter(namespace, name, 
'friend')
         # Get matching functions but only ones where the parent is not a 
group. We want to skip
         # function entries in groups as we'll find the same functions in a 
file's xml output
         # elsewhere and having more than one match is confusing for our logic 
later on.
-        return function_filter & ~(parent_is_compound & parent_is_group)
+        return (function_filter | friend_filter) & ~(parent_is_compound & 
parent_is_group)
 
     def create_enumvalue_finder_filter(self, name: str) -> Filter:
         """Returns a filter which looks for an enumvalue with the specified 
name."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/breathe/renderer/sphinxrenderer.py 
new/breathe-4.27.0/breathe/renderer/sphinxrenderer.py
--- old/breathe-4.24.1/breathe/renderer/sphinxrenderer.py       2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/breathe/renderer/sphinxrenderer.py       2021-02-16 
16:26:15.000000000 +0100
@@ -148,6 +148,26 @@
 
 # ----------------------------------------------------------------------------
 
+# Create multi-inheritance classes to merge BaseObject from Breathe with
+# classes from phpdomain.
+# We use capitalization (and the namespace) to differentiate between the two
+
+if php is not None:
+    class PHPNamespaceLevel(BaseObject, php.PhpNamespacelevel):
+        """Description of a PHP item *in* a namespace (not the space 
itself)."""
+        pass
+
+    class PHPClassLike(BaseObject, php.PhpClasslike):
+        pass
+
+    class PHPClassMember(BaseObject, php.PhpClassmember):
+        pass
+
+    class PHPGlobalLevel(BaseObject, php.PhpGloballevel):
+        pass
+
+# ----------------------------------------------------------------------------
+
 if cs is not None:
     class CSharpCurrentNamespace(BaseObject, cs.CSharpCurrentNamespace):
         pass
@@ -239,12 +259,13 @@
 
     if php is not None:
         php_classes = {
-            'function': (php.PhpNamespacelevel, 'function'),
-            'class': (php.PhpClasslike, 'class'),
-            'attr': (php.PhpClassmember, 'attr'),
-            'method': (php.PhpClassmember, 'method'),
-            'global': (php.PhpGloballevel, 'global'),
+            'function': (PHPNamespaceLevel, 'function'),
+            'class': (PHPClassLike, 'class'),
+            'attr': (PHPClassMember, 'attr'),
+            'method': (PHPClassMember, 'method'),
+            'global': (PHPGlobalLevel, 'global'),
         }
+        php_classes_default = php_classes['class']  # Directive when no 
matching ones were found
 
     if cs is not None:
         cs_classes = {
@@ -291,8 +312,12 @@
             else:
                 if arg_0 in ['variable']:
                     arg_0 = 'global'
-            cls, name = DomainDirectiveFactory.php_classes.get(
-                arg_0, (php.PhpClasslike, 'class'))
+
+            if arg_0 in DomainDirectiveFactory.php_classes:
+                cls, name = DomainDirectiveFactory.php_classes[arg_0]  # type: 
ignore
+            else:
+                cls, name = DomainDirectiveFactory.php_classes_default  # 
type: ignore
+
         elif cs is not None and domain == 'cs':
             cls, name = DomainDirectiveFactory.cs_classes[args[0]]  # type: 
ignore
         else:
@@ -1165,13 +1190,13 @@
 
         # Get all sub sections
         for sectiondef in node.sectiondef:
+            kind = sectiondef.kind
+            if section_order is not None and kind not in section_order:
+                continue
             child_nodes = self.render(sectiondef)
             if not child_nodes:
                 # Skip empty section
                 continue
-            kind = sectiondef.kind
-            if section_order is not None and kind not in section_order:
-                continue
             rst_node = nodes.container(classes=['breathe-sectiondef'])
             rst_node.document = self.state.document
             rst_node['objtype'] = kind
@@ -1284,6 +1309,9 @@
 
         return [nodes.paragraph("", "", *nodelist)]
 
+    def visit_docparblock(self, node) -> List[Node]:
+        return self.render_iterable(node.para)
+
     def visit_docimage(self, node) -> List[Node]:
         """Output docutils image node using name attribute from xml as the 
uri"""
 
@@ -1316,8 +1344,20 @@
             print("Warning: does not currently handle 'small' text display")
         return [creator("", "", *nodelist)]
 
-    def visit_docsect1(self, node) -> List[Node]:
-        return []
+    def visit_docsectN(self, node) -> List[Node]:
+        '''
+        Docutils titles are defined by their level inside the document so
+        the proper structure is only guaranteed by the Doxygen XML.
+
+        Doxygen command mapping to XML element name:
+        @section == sect1, @subsection == sect2, @subsubsection == sect3
+        '''
+        section = nodes.section()
+        section['ids'].append(self.get_refid(node.id))
+        section += self.create_doxygen_target(node)
+        section += nodes.title(node.title, node.title)
+        section += self.render_iterable(node.content_)
+        return [section]
 
     def visit_docsimplesect(self, node) -> List[Node]:
         """Other Type documentation such as Warning, Note, Returns, etc"""
@@ -1582,9 +1622,39 @@
         content = node.term.content_
         return self.render_iterable(content)
 
-    def visit_docanchor(self, node) -> List[None]:
+    def visit_docanchor(self, node) -> List[Node]:
         return self.create_doxygen_target(node)
 
+    def visit_docentry(self, node) -> List[Node]:
+        col = nodes.entry()
+        col += self.render_iterable(node.para)
+        if node.thead == 'yes':
+            col['heading'] = True
+        return [col]
+
+    def visit_docrow(self, node) -> List[Node]:
+        row = nodes.row()
+        cols = self.render_iterable(node.entry)
+        if all(col.get('heading', False) for col in cols):
+            elem = nodes.thead()
+        else:
+            elem = nodes.tbody()
+        row += cols
+        elem += row
+        return [elem]
+
+    def visit_doctable(self, node) -> List[Node]:
+        table = nodes.table()
+        table['classes'] += ['colwidths-auto']
+        tgroup = nodes.tgroup(cols=node.cols)
+        for _ in range(node.cols):
+            colspec = nodes.colspec()
+            colspec.attributes['colwidth'] = 'auto'
+            tgroup += colspec
+        table += tgroup
+        tgroup += self.render_iterable(node.row)
+        return [table]
+
     def visit_mixedcontainer(self, node) -> List[Node]:
         return self.render_optional(node.getValue())
 
@@ -1603,12 +1673,25 @@
             if dom == 'py':
                 declaration = name + node.get_argsstring()
             else:
-                declaration = ' '.join([
-                    self.create_template_prefix(node),
-                    ''.join(n.astext() for n in self.render(node.get_type())), 
 # type: ignore
-                    name,
-                    node.get_argsstring()
-                ])
+                elements = [self.create_template_prefix(node)]
+                if node.static == 'yes':
+                    elements.append('static')
+                if node.inline == 'yes':
+                    elements.append('inline')
+                if node.kind == 'friend':
+                    elements.append('friend')
+                if node.virt in ('virtual', 'pure-virtual'):
+                    elements.append('virtual')
+                if node.explicit == 'yes':
+                    elements.append('explicit')
+                if 'constexpr' in dir(node):
+                    assert node.constexpr == 'yes'
+                    elements.append('constexpr')
+                elements.append(''.join(n.astext()
+                                        for n in 
self.render(node.get_type())))  # type: ignore
+                elements.append(name)
+                elements.append(node.get_argsstring())
+                declaration = ' '.join(elements)
             nodes = self.handle_declaration(node, declaration)
             return nodes
         else:
@@ -1753,16 +1836,19 @@
             if len(initializer) != 0:
                 options['value'] = initializer
         else:
+            elements = [self.create_template_prefix(node)]
+            if node.static == 'yes':
+                elements.append('static')
+            if node.mutable == 'yes':
+                elements.append('mutable')
             typename = ''.join(n.astext() for n in 
self.render(node.get_type()))
             if dom == 'c' and '::' in typename:
                 typename = typename.replace('::', '.')
-            declaration = ' '.join([
-                self.create_template_prefix(node),
-                typename,
-                name,
-                node.get_argsstring(),
-                self.make_initializer(node)
-            ])
+            elements.append(typename)
+            elements.append(name)
+            elements.append(node.get_argsstring())
+            elements.append(self.make_initializer(node))
+            declaration = ' '.join(elements)
         if not dom or dom in ('c', 'cpp', 'py', 'cs'):
             return self.handle_declaration(node, declaration, options=options)
         else:
@@ -1778,7 +1864,11 @@
         desc += signode
 
         typ = ''.join(n.astext() for n in self.render(node.get_type()))  # 
type: ignore
-        assert typ in ("friend class", "friend struct")
+        # in Doxygen < 1.9 the 'friend' part is there, but afterwards not
+        # https://github.com/michaeljones/breathe/issues/616
+        assert typ in ("friend class", "friend struct", "class", "struct")
+        if not typ.startswith('friend '):
+            typ = 'friend ' + typ
         signode += addnodes.desc_annotation(typ, typ)
         signode += nodes.Text(' ')
         # expr = cpp.CPPExprRole(asCode=False)
@@ -1934,10 +2024,13 @@
         "docreftext": visit_docreftext,
         "docheading": visit_docheading,
         "docpara": visit_docpara,
+        "docparblock": visit_docparblock,
         "docimage": visit_docimage,
         "docurllink": visit_docurllink,
         "docmarkup": visit_docmarkup,
-        "docsect1": visit_docsect1,
+        "docsect1": visit_docsectN,
+        "docsect2": visit_docsectN,
+        "docsect3": visit_docsectN,
         "docsimplesect": visit_docsimplesect,
         "doctitle": visit_doctitle,
         "docformula": visit_docformula,
@@ -1964,6 +2057,9 @@
         "docvariablelist": visit_docvariablelist,
         "docvarlistentry": visit_docvarlistentry,
         "docanchor": visit_docanchor,
+        "doctable": visit_doctable,
+        "docrow": visit_docrow,
+        "docentry": visit_docentry,
     }
 
     def render(self, node, context: Optional[RenderContext] = None) -> 
List[Node]:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/documentation/source/conf.py 
new/breathe-4.27.0/documentation/source/conf.py
--- old/breathe-4.24.1/documentation/source/conf.py     2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/documentation/source/conf.py     2021-02-16 
16:26:15.000000000 +0100
@@ -204,6 +204,7 @@
     "programlisting":"../../examples/specific/programlisting/xml/",
     "image":"../../examples/specific/image/xml/",
     "lists":"../../examples/specific/lists/xml/",
+    "tables":"../../examples/specific/tables/xml/",
     "group":"../../examples/specific/group/xml/",
     "union":"../../examples/specific/union/xml/",
     "qtsignalsandslots":"../../examples/specific/qtsignalsandslots/xml/",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/documentation/source/directives.rst 
new/breathe-4.27.0/documentation/source/directives.rst
--- old/breathe-4.24.1/documentation/source/directives.rst      2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/documentation/source/directives.rst      2021-02-16 
16:26:15.000000000 +0100
@@ -438,6 +438,12 @@
               "h" : "cpp",
               }
 
+   You can also use this to enable support for Doxygen XML generated from PHP 
code::
+
+      breathe_domain_by_extension = {
+              "php" : "php",
+              }
+
 .. confval:: breathe_domain_by_file_pattern
 
    Allows you to specify domains for particular files by wildcard syntax. This
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/documentation/source/doxygen.rst 
new/breathe-4.27.0/documentation/source/doxygen.rst
--- old/breathe-4.24.1/documentation/source/doxygen.rst 2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/documentation/source/doxygen.rst 2021-02-16 
16:26:15.000000000 +0100
@@ -84,6 +84,14 @@
 .. doxygenindex::
    :path: ../../examples/doxygen/par/xml
 
+Parblock
+--------
+
+.. cpp:namespace:: @ex_doxygen_parblock
+
+.. doxygenindex::
+   :path: ../../examples/doxygen/parblock/xml
+
 Overload
 --------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/documentation/source/index.rst 
new/breathe-4.27.0/documentation/source/index.rst
--- old/breathe-4.24.1/documentation/source/index.rst   2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/documentation/source/index.rst   2021-02-16 
16:26:15.000000000 +0100
@@ -69,6 +69,7 @@
    customcss
    groups
    lists
+   tables
    template
 
 Contributing
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/documentation/source/tables.rst 
new/breathe-4.27.0/documentation/source/tables.rst
--- old/breathe-4.24.1/documentation/source/tables.rst  1970-01-01 
01:00:00.000000000 +0100
+++ new/breathe-4.27.0/documentation/source/tables.rst  2021-02-16 
16:26:15.000000000 +0100
@@ -0,0 +1,89 @@
+Tables
+======
+
+Breathe has support for tables in the doxygen documentation. They are output as
+follows.
+
+.. cpp:namespace:: @ex_tables_simple
+
+A simple Markdown syntax table ::
+
+   .. doxygenclass:: Table_1
+      :project: tables
+
+It renders as:
+
+----
+
+.. doxygenclass:: Table_1
+   :project: tables
+
+----
+
+.. cpp:namespace:: @ex_tables_aligned
+
+A Markdown syntax table with alignment ::
+
+   .. doxygenclass:: Table_2
+      :project: tables
+
+It renders as:
+
+----
+
+.. doxygenclass:: Table_2
+   :project: tables
+
+----
+
+.. cpp:namespace:: @ex_tables_rowspan
+
+A Markdown syntax table with rowspan (and alignment) ::
+
+   .. doxygenclass:: Table_3
+      :project: tables
+
+It renders as:
+
+----
+
+.. only:: html
+
+    .. doxygenclass:: Table_3
+       :project: tables
+
+----
+
+.. cpp:namespace:: @ex_tables_colspan
+
+A Markdown syntax table with colspan (and alignment) ::
+
+   .. doxygenclass:: Table_4
+      :project: tables
+
+It renders as:
+
+----
+
+.. only:: html
+
+    .. doxygenclass:: Table_4
+       :project: tables
+
+----
+
+.. cpp:namespace:: @ex_tables_doxygen
+
+A Doxygen syntax table ::
+
+   .. doxygenclass:: Table_5
+      :project: tables
+
+It renders as:
+
+----
+
+.. only:: html
+
+    .. doxygenclass:: Table_5
+       :project: tables
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/doxygen/.gitignore 
new/breathe-4.27.0/examples/doxygen/.gitignore
--- old/breathe-4.24.1/examples/doxygen/.gitignore      2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/examples/doxygen/.gitignore      2021-02-16 
16:26:15.000000000 +0100
@@ -18,6 +18,7 @@
 overload
 page
 par
+parblock
 pyexample
 qtstyle
 relates
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/doxygen/Makefile 
new/breathe-4.27.0/examples/doxygen/Makefile
--- old/breathe-4.24.1/examples/doxygen/Makefile        2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/examples/doxygen/Makefile        2021-02-16 
16:26:15.000000000 +0100
@@ -27,6 +27,7 @@
      relates/xml/index.xml \
      author/xml/index.xml \
      par/xml/index.xml \
+     parblock/xml/index.xml \
      overload/xml/index.xml \
      example/xml/index.xml \
      include/xml/index.xml \
@@ -49,7 +50,7 @@
 
 clean:
        rm -rf  class define enum file func page relates author \
-                par overload example include qtstyle jdstyle structcmd \
+               par parblock overload example include qtstyle jdstyle structcmd 
\
                autolink tag restypedef afterdoc template tag group diagrams \
                memgrp docstring pyexample mux manual interface
 
@@ -80,6 +81,9 @@
 par/xml/index.xml: par.cpp par.cfg
        $(DOXYGEN) par.cfg
 
+parblock/xml/index.xml: parblock.cpp parblock.cfg
+       $(DOXYGEN) parblock.cfg
+
 overload/xml/index.xml: overload.cpp overload.cfg
        $(DOXYGEN) overload.cfg
 
@@ -140,4 +144,3 @@
 ifneq ($(HAVE_DOT),)
        $(DOXYGEN) diagrams.cfg
 endif
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/doxygen/parblock.cfg 
new/breathe-4.27.0/examples/doxygen/parblock.cfg
--- old/breathe-4.24.1/examples/doxygen/parblock.cfg    1970-01-01 
01:00:00.000000000 +0100
+++ new/breathe-4.27.0/examples/doxygen/parblock.cfg    2021-02-16 
16:26:15.000000000 +0100
@@ -0,0 +1,11 @@
+PROJECT_NAME     = "Parblock Command"
+OUTPUT_DIRECTORY = parblock
+GENERATE_LATEX   = NO
+GENERATE_MAN     = NO
+GENERATE_RTF     = NO
+CASE_SENSE_NAMES = NO
+INPUT            = parblock.cpp
+QUIET            = YES
+JAVADOC_AUTOBRIEF = YES
+GENERATE_HTML = NO
+GENERATE_XML = YES
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/doxygen/parblock.cpp 
new/breathe-4.27.0/examples/doxygen/parblock.cpp
--- old/breathe-4.24.1/examples/doxygen/parblock.cpp    1970-01-01 
01:00:00.000000000 +0100
+++ new/breathe-4.27.0/examples/doxygen/parblock.cpp    2021-02-16 
16:26:15.000000000 +0100
@@ -0,0 +1,24 @@
+/*! \class Test15
+ * Normal text.
+ *
+ * \par A paragraph followed by a paragraph block:
+ * \parblock
+ * Contents of the first paragraph in the block.
+ *
+ * New paragraph under the same heading.
+ * \endparblock
+ *
+ * \note
+ * This note consists of three paragraphs in a block.
+ * \parblock
+ * This is the first paragraph in the block.
+ *
+ * And this is the second paragraph in the block.
+ *
+ * And still a third paragraph in the block.
+ * \endparblock
+ *
+ * More normal text.
+ */
+  
+class Test15 {};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/specific/Makefile 
new/breathe-4.27.0/examples/specific/Makefile
--- old/breathe-4.24.1/examples/specific/Makefile       2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/examples/specific/Makefile       2021-02-16 
16:26:15.000000000 +0100
@@ -23,7 +23,7 @@
                        image name union group struct struct_function 
qtsignalsandslots lists \
                        headings links parameters template_class 
template_class_non_type \
                        template_function template_type_alias 
template_specialisation \
-                       enum define interface xrefsect \
+                       enum define interface xrefsect tables \
                        cpp_anon cpp_enum cpp_union cpp_function 
cpp_friendclass \
                        cpp_inherited_members cpp_trailing_return_type \
                        c_file c_struct c_enum c_typedef c_macro c_union
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/specific/tables.cfg 
new/breathe-4.27.0/examples/specific/tables.cfg
--- old/breathe-4.24.1/examples/specific/tables.cfg     1970-01-01 
01:00:00.000000000 +0100
+++ new/breathe-4.27.0/examples/specific/tables.cfg     2021-02-16 
16:26:15.000000000 +0100
@@ -0,0 +1,11 @@
+PROJECT_NAME     = "Tables Option"
+OUTPUT_DIRECTORY = tables
+GENERATE_LATEX   = NO
+GENERATE_MAN     = NO
+GENERATE_RTF     = NO
+CASE_SENSE_NAMES = NO
+INPUT            = tables.h
+QUIET            = YES
+JAVADOC_AUTOBRIEF = YES
+GENERATE_HTML = NO
+GENERATE_XML = YES
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/examples/specific/tables.h 
new/breathe-4.27.0/examples/specific/tables.h
--- old/breathe-4.24.1/examples/specific/tables.h       1970-01-01 
01:00:00.000000000 +0100
+++ new/breathe-4.27.0/examples/specific/tables.h       2021-02-16 
16:26:15.000000000 +0100
@@ -0,0 +1,96 @@
+/**
+ * \brief This is a simple Markdown table example.
+ *
+ * Following is a simple table using Markdown syntax.
+ *
+ * First Header  | Second Header
+ * ------------- | -------------
+ * Content Cell  | Content Cell
+ * Content Cell  | Content Cell
+ *
+ * And this is some more text.
+ */
+class Table_1
+{
+};
+
+/**
+ * \brief This is a Markdown table with alignment.
+ *
+ * Following is a table with alignment using Markdown syntax.
+ *
+ * | Right | Center | Left  |
+ * | ----: | :----: | :---- |
+ * | 10    | 10     | 10    |
+ * | 1000  | 1000   | 1000  |
+ *
+ * And this is some more text.
+ */
+class Table_2
+{
+};
+
+/**
+ * \brief This is a Markdown table with rowspan and alignment.
+ *
+ * Following is a table with rowspan and alignment using Markdown syntax.
+ *
+ * | Right | Center | Left  |
+ * | ----: | :----: | :---- |
+ * | 10    | 10     | 10    |
+ * | ^     | 1000   | 1000  |
+ *
+ * And this is some more text.
+ */
+class Table_3
+{
+};
+
+/**
+ * \brief This is a Markdown table with colspan and alignment.
+ *
+ * Following is a table with colspan and alignment using Markdown syntax.
+ *
+ * | Right | Center | Left  |
+ * | ----: | :----: | :---- |
+ * | 10    | 10     | 10    |
+ * | 1000  |||
+ *
+ * And this is some more text.
+ */
+class Table_4
+{
+};
+
+/**
+ * \brief This is a Doxygen table.
+ *
+ * Following is a table using Doxygen syntax (and all supported features).
+ *
+ * <table>
+ * <caption id="multi_row">Complex table</caption>
+ * <tr><th>Column 1                      <th>Column 2        <th>Column 3
+ * <tr><td rowspan="2">cell row=1+2,col=1<td>cell row=1,col=2<td>cell 
row=1,col=3
+ * <tr><td rowspan="2">cell row=2+3,col=2                    <td>cell 
row=2,col=3
+ * <tr><td>cell row=3,col=1                                  <td 
rowspan="2">cell row=3+4,col=3
+ * <tr><td colspan="2">cell row=4,col=1+2
+ * <tr><td>cell row=5,col=1              <td colspan="2">cell row=5,col=2+3
+ * <tr><td colspan="2" rowspan="2">cell row=6+7,col=1+2      <td>cell 
row=6,col=3
+ * <tr>                                                      <td>cell 
row=7,col=3
+ * <tr><td>cell row=8,col=1              <td>cell row=8,col=2\n
+ *   <table>
+ *     <tr><td>Inner cell row=1,col=1<td>Inner cell row=1,col=2
+ *     <tr><td>Inner cell row=2,col=1<td>Inner cell row=2,col=2
+ *   </table>
+ *   <td>cell row=8,col=3
+ *   <ul>
+ *     <li>Item 1
+ *     <li>Item 2
+ *   </ul>
+ * </table>
+ *
+ * And this is some more text.
+ */
+class Table_5
+{
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/requirements/production.txt 
new/breathe-4.27.0/requirements/production.txt
--- old/breathe-4.24.1/requirements/production.txt      2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/requirements/production.txt      2021-02-16 
16:26:15.000000000 +0100
@@ -2,5 +2,5 @@
 Jinja2>=2.7.3
 MarkupSafe>=0.23
 Pygments>=1.6
-Sphinx>=3.0,<3.4
+Sphinx>=3.0,<3.6
 six>=1.9.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/setup.py new/breathe-4.27.0/setup.py
--- old/breathe-4.24.1/setup.py 2020-12-01 22:19:50.000000000 +0100
+++ new/breathe-4.27.0/setup.py 2021-02-16 16:26:15.000000000 +0100
@@ -14,7 +14,7 @@
  render `Doxygen <http://www.doxygen.org>`__ xml output.
 '''
 
-requires = ['Sphinx>=3.0,<3.4', 'docutils>=0.12', 'six>=1.9']
+requires = ['Sphinx>=3.0,<3.6', 'docutils>=0.12', 'six>=1.9']
 
 if sys.version_info < (3, 5):
     print('ERROR: Sphinx requires at least Python 3.5 to run.')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/tests/data/arange.xml 
new/breathe-4.27.0/tests/data/arange.xml
--- old/breathe-4.24.1/tests/data/arange.xml    1970-01-01 01:00:00.000000000 
+0100
+++ new/breathe-4.27.0/tests/data/arange.xml    2021-02-16 16:26:15.000000000 
+0100
@@ -0,0 +1,149 @@
+<sectiondef kind="typedef">
+<memberdef kind="function" id="1" prot="public" static="no" const="yes" 
explicit="no" inline="no" virt="non-virtual">
+  <type><ref refid="classat_1_1_tensor" kindref="compound">Tensor</ref></type>
+  <definition>Tensor at::arange</definition>
+  <argsstring>(Scalar end, const TensorOptions &amp;options={})</argsstring>
+  <name>arange</name>
+  <param>
+    <type>Scalar</type>
+    <declname>end</declname>
+  </param>
+  <param>
+    <type>const TensorOptions &amp;</type>
+    <declname>options</declname>
+    <defval>{}</defval>
+  </param>
+</memberdef>
+<memberdef kind="function" id="2" prot="public" static="no" const="yes" 
explicit="no" inline="no" virt="non-virtual">
+  <type><ref refid="classat_1_1_tensor" kindref="compound">Tensor</ref></type>
+  <definition>Tensor at::arange</definition>
+  <argsstring>(Scalar end, c10::optional&lt; ScalarType &gt; dtype, 
c10::optional&lt; Layout &gt; layout, c10::optional&lt; Device &gt; device, 
c10::optional&lt; bool &gt; pin_memory)</argsstring>
+  <name>arange</name>
+  <param>
+    <type>Scalar</type>
+    <declname>end</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; ScalarType &gt;</type>
+    <declname>dtype</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; Layout &gt;</type>
+    <declname>layout</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; Device &gt;</type>
+    <declname>device</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; bool &gt;</type>
+    <declname>pin_memory</declname>
+  </param>
+</memberdef>
+<memberdef kind="function" id="3" prot="public" static="no" const="no" 
explicit="no" inline="no" virt="non-virtual">
+  <type><ref refid="classat_1_1_tensor" kindref="compound">Tensor</ref></type>
+  <definition>Tensor at::arange</definition>
+  <argsstring>(Scalar start, Scalar end, const TensorOptions 
&amp;options={})</argsstring>
+  <name>arange</name>
+  <param>
+    <type>Scalar</type>
+    <declname>start</declname>
+  </param>
+  <param>
+    <type>Scalar</type>
+    <declname>end</declname>
+  </param>
+  <param>
+    <type>const TensorOptions &amp;</type>
+    <declname>options</declname>
+    <defval>{}</defval>
+  </param>
+</memberdef>
+<memberdef kind="function" id="4" prot="public" static="no" const="no" 
explicit="no" inline="no" virt="non-virtual">
+  <type><ref refid="classat_1_1_tensor" kindref="compound">Tensor</ref></type>
+  <definition>Tensor at::arange</definition>
+  <argsstring>(Scalar start, Scalar end, c10::optional&lt; ScalarType &gt; 
dtype, c10::optional&lt; Layout &gt; layout, c10::optional&lt; Device &gt; 
device, c10::optional&lt; bool &gt; pin_memory)</argsstring>
+  <name>arange</name>
+  <param>
+    <type>Scalar</type>
+    <declname>start</declname>
+  </param>
+  <param>
+    <type>Scalar</type>
+    <declname>end</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; ScalarType &gt;</type>
+    <declname>dtype</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; Layout &gt;</type>
+    <declname>layout</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; Device &gt;</type>
+    <declname>device</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; bool &gt;</type>
+    <declname>pin_memory</declname>
+  </param>
+</memberdef>
+<memberdef kind="function" id="5" prot="public" static="no" const="no" 
explicit="no" inline="no" virt="non-virtual">
+  <type><ref refid="classat_1_1_tensor" kindref="compound">Tensor</ref></type>
+  <definition>Tensor at::arange</definition>
+  <argsstring>(Scalar start, Scalar end, Scalar step, const TensorOptions 
&amp;options={})</argsstring>
+  <name>range</name>
+  <param>
+    <type>Scalar</type>
+    <declname>start</declname>
+  </param>
+  <param>
+    <type>Scalar</type>
+    <declname>end</declname>
+  </param>
+  <param>
+    <type>Scalar</type>
+    <declname>step</declname>
+  </param>
+  <param>
+    <type>const TensorOptions &amp;</type>
+    <declname>options</declname>
+    <defval>{}</defval>
+  </param>
+</memberdef>
+<memberdef kind="function" id="6" prot="public" static="no" const="no" 
explicit="no" inline="no" virt="non-virtual">
+  <type><ref refid="classat_1_1_tensor" kindref="compound">Tensor</ref></type>
+  <definition>Tensor at::arange</definition>
+  <argsstring>(Scalar start, Scalar end, Scalar step, c10::optional&lt; 
ScalarType &gt; dtype, c10::optional&lt; Layout &gt; layout, c10::optional&lt; 
Device &gt; device, c10::optional&lt; bool &gt; pin_memory)</argsstring>
+  <name>range</name>
+  <param>
+    <type>Scalar</type>
+    <declname>start</declname>
+  </param>
+  <param>
+    <type>Scalar</type>
+    <declname>end</declname>
+  </param>
+  <param>
+    <type>Scalar</type>
+    <declname>step</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; ScalarType &gt;</type>
+    <declname>dtype</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; Layout &gt;</type>
+    <declname>layout</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; Device &gt;</type>
+    <declname>device</declname>
+  </param>
+  <param>
+    <type><ref refid="classc10_1_1optional" 
kindref="compound">c10::optional</ref>&lt; bool &gt;</type>
+    <declname>pin_memory</declname>
+  </param>
+</memberdef>
+</sectiondef>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/tests/data/ellipsis.xml 
new/breathe-4.27.0/tests/data/ellipsis.xml
--- old/breathe-4.24.1/tests/data/ellipsis.xml  1970-01-01 01:00:00.000000000 
+0100
+++ new/breathe-4.27.0/tests/data/ellipsis.xml  2021-02-16 16:26:15.000000000 
+0100
@@ -0,0 +1,20 @@
+<sectiondef kind="typedef">
+<memberdef kind="function" id="0" prot="public" static="no" const="no" 
explicit="no" inline="no" virt="non-virtual">
+  <type>double</type>
+  <definition>double amici::spline_pos</definition>
+  <argsstring>(double t, int num,...)</argsstring>
+  <name>spline_pos</name>
+  <param>
+    <type>double</type>
+    <declname>t</declname>
+  </param>
+  <param>
+    <type>int</type>
+    <declname>num</declname>
+  </param>
+  <param>
+    <type>...</type>
+  </param>
+</memberdef>
+</sectiondef>
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/tests/test_renderer.py 
new/breathe-4.27.0/tests/test_renderer.py
--- old/breathe-4.24.1/tests/test_renderer.py   2020-12-01 22:19:50.000000000 
+0100
+++ new/breathe-4.27.0/tests/test_renderer.py   2021-02-16 16:26:15.000000000 
+0100
@@ -1,3 +1,4 @@
+import os
 import pytest  # type: ignore
 
 import sphinx.addnodes
@@ -403,3 +404,67 @@
                for el in render(app, compound_def,
                                 compound_parser=mock_compound_parser,
                                 options=['inner']))
+
+def get_directive(app):
+    from breathe.directives 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'],
+                {'members': '', 'protected-members': None, 'undoc-members': 
None},
+                StringList([], items=[]),
+                20, 24,
+                ('.. doxygenclass:: at::Tensor\n   :members:\n'
+                        '   :protected-members:\n   :undoc-members:'),
+                MockState(app),
+                MockStateMachine(),
+               )
+    return DoxygenFunctionDirective(finder_factory, project_info_factory, 
parser_factory, *cls_args)
+
+
+def get_matches(datafile):
+    from breathe.parser.compoundsuper import sectiondefType
+    from xml.dom import minidom
+
+    argsstrings = []
+    with open(os.path.join(os.path.dirname(__file__), 'data', datafile)) as 
fid:
+        xml = fid.read()
+    doc = minidom.parseString(xml)
+
+    sectiondef = sectiondefType.factory()
+    for child in doc.documentElement.childNodes:
+        sectiondef.buildChildren(child, 'memberdef')
+        if getattr(child, 'tagName', None) == 'memberdef':
+            # Get the argsstring function declaration
+            
argsstrings.append(child.getElementsByTagName('argsstring')[0].childNodes[0].data)
+    matches = [[m, sectiondef] for m in sectiondef.memberdef]
+    return argsstrings, matches
+
+
+def test_resolve_overrides(app):
+    # Test that multiple function overrides works
+    argsstrings, matches = get_matches('arange.xml')
+    cls = get_directive(app)
+
+    # Verify that the exact arguments returns one override
+    for args in argsstrings:
+        ast_param = cls._parse_args(args)
+        ret = cls._resolve_function(matches, ast_param, None)
+
+def test_ellipsis(app):
+    argsstrings, matches = get_matches('ellipsis.xml')
+    cls = get_directive(app)
+
+    # Verify that parsing an ellipsis works
+    ast_param = cls._parse_args(argsstrings[0])
+    ret = cls._resolve_function(matches, ast_param, None)
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/breathe-4.24.1/tests/warnings/source/conf.py 
new/breathe-4.27.0/tests/warnings/source/conf.py
--- old/breathe-4.24.1/tests/warnings/source/conf.py    2020-12-01 
22:19:50.000000000 +0100
+++ new/breathe-4.27.0/tests/warnings/source/conf.py    2021-02-16 
16:26:15.000000000 +0100
@@ -11,6 +11,8 @@
 #
 # All configuration values have a default; values that are commented out
 # serve to show the default.
+#
+# mypy: ignore-errors
 
 import sys
 import os

Reply via email to