mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
8756e68bff
OpenCV New Documentation #29206 This PR introduces end to end working new Documentation in OpenCV. Co-authored by: @abhishek-gola @omrope79 @Akansha-977 @Prasadayus @varun-jaiswal17 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
172 lines
5.3 KiB
Python
172 lines
5.3 KiB
Python
# This file is part of OpenCV project.
|
|
# It is subject to the license terms in the LICENSE file found in the top-level directory
|
|
# of this distribution and at http://opencv.org/license.html.
|
|
# Copyright (C) 2026, BigVision LLC, all rights reserved.
|
|
# Third party copyrights are property of their respective owners.
|
|
|
|
"""Runtime patches for Sphinx C++ domain and breathe; applied at import."""
|
|
from __future__ import annotations
|
|
|
|
def _patch_cpp_xref_resolver():
|
|
"""Work around Sphinx 8.1.x parentSymbol assert in _resolve_xref_inner."""
|
|
try:
|
|
from sphinx.domains.cpp import CPPDomain
|
|
except ImportError:
|
|
return
|
|
original = CPPDomain._resolve_xref_inner
|
|
|
|
def guarded(self, env, fromdocname, builder, typ, target, node, contnode):
|
|
try:
|
|
return original(self, env, fromdocname, builder, typ, target,
|
|
node, contnode)
|
|
except AssertionError:
|
|
return None, None
|
|
CPPDomain._resolve_xref_inner = guarded
|
|
|
|
# Drop breathe unresolvable-xref log noise; text still renders.
|
|
import logging
|
|
_UNRESOLVED_XREF_PATTERNS = (
|
|
"Unable to resolve function",
|
|
"Unable to resolve class",
|
|
"Cannot find function",
|
|
"Cannot find class",
|
|
"Cannot find variable",
|
|
"Cannot find typedef",
|
|
"Cannot find enum",
|
|
"Cannot find enumerator",
|
|
"Cannot find define",
|
|
"Duplicate C++ declaration",
|
|
)
|
|
|
|
class _UnresolvedXrefFilter(logging.Filter):
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
msg = record.getMessage()
|
|
return not any(p in msg for p in _UNRESOLVED_XREF_PATTERNS)
|
|
|
|
_filt = _UnresolvedXrefFilter()
|
|
for _logger_name in ("sphinx", "docutils"):
|
|
logging.getLogger(_logger_name).addFilter(_filt)
|
|
|
|
|
|
_patch_cpp_xref_resolver()
|
|
|
|
|
|
def _silence_breathe_anon_enum_warning():
|
|
"""Mute Sphinx parser warning on Doxygen's anonymous nested enums."""
|
|
import logging
|
|
class _AnonEnumFilter(logging.Filter):
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
msg = record.getMessage()
|
|
return not (
|
|
"Invalid C++ declaration" in msg
|
|
and "Expected identifier in nested name" in msg
|
|
)
|
|
for _name in ("sphinx", "docutils"):
|
|
logging.getLogger(_name).addFilter(_AnonEnumFilter())
|
|
|
|
|
|
_silence_breathe_anon_enum_warning()
|
|
|
|
|
|
def _patch_breathe_operator_signatures():
|
|
"""Fix breathe {doxygenfunction} mis-splitting operator overloads."""
|
|
try:
|
|
import breathe.directives.function as _bf
|
|
except ImportError:
|
|
return
|
|
|
|
def _split_operator(s: str):
|
|
rp = s.rfind(")")
|
|
if rp == -1:
|
|
return None
|
|
depth, j = 0, rp
|
|
while j >= 0:
|
|
if s[j] == ")":
|
|
depth += 1
|
|
elif s[j] == "(":
|
|
depth -= 1
|
|
if depth == 0:
|
|
break
|
|
j -= 1
|
|
if j < 0:
|
|
return None
|
|
func_part, args_part = s[:j].strip(), s[j:]
|
|
k = func_part.find("::operator")
|
|
if k != -1:
|
|
return func_part[:k], func_part[k + 2:], args_part
|
|
if "::" in func_part:
|
|
ns, fn = func_part.rsplit("::", 1)
|
|
return ns, fn, args_part
|
|
return "", func_part, args_part
|
|
|
|
class _Shim:
|
|
__slots__ = ("_g",)
|
|
|
|
def __init__(self, g1, g2, g3):
|
|
self._g = (None, g1, g2, g3)
|
|
|
|
def group(self, i=0):
|
|
return self._g[i]
|
|
|
|
class _OperatorAwareRe:
|
|
def __init__(self, real):
|
|
object.__setattr__(self, "_real", real)
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self._real, name)
|
|
|
|
def match(self, pattern, string, *args, **kwargs):
|
|
m = self._real.match(pattern, string, *args, **kwargs)
|
|
if (m is not None and getattr(m.re, "groups", 0) >= 3
|
|
and "::operator" in string):
|
|
res = _split_operator(string)
|
|
if res is not None:
|
|
ns, fn, ar = res
|
|
return _Shim(ns or None, fn, ar)
|
|
return m
|
|
|
|
if not isinstance(_bf.re, _OperatorAwareRe):
|
|
_bf.re = _OperatorAwareRe(_bf.re)
|
|
|
|
|
|
_patch_breathe_operator_signatures()
|
|
|
|
|
|
def _patch_breathe_docsect():
|
|
"""Render title-less docSectN nodes breathe 4.36 drops."""
|
|
try:
|
|
from breathe.renderer import sphinxrenderer as _bsr
|
|
except ImportError:
|
|
return
|
|
_methods = _bsr.SphinxRenderer.methods
|
|
if getattr(_methods.get("docsect1"), "_opencv_docsect_patch", False):
|
|
return
|
|
_orig_visit = _methods["docsect1"]
|
|
|
|
def _visit_docsectN(self, node):
|
|
if not getattr(node, "title", None):
|
|
return self.render_iterable(node.content_)
|
|
return _orig_visit(self, node)
|
|
|
|
_visit_docsectN._opencv_docsect_patch = True
|
|
for _kind in ("docsect1", "docsect2", "docsect3"):
|
|
_methods[_kind] = _visit_docsectN
|
|
|
|
|
|
_patch_breathe_docsect()
|
|
|
|
|
|
def _silence_orphan_toctree_warning():
|
|
"""Mute toctree-orphan warning for intentionally unlinked external pages."""
|
|
import logging
|
|
|
|
class _OrphanFilter(logging.Filter):
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
return "included in any toctree" not in record.getMessage()
|
|
|
|
for _name in ("sphinx", "docutils"):
|
|
logging.getLogger(_name).addFilter(_OrphanFilter())
|
|
|
|
|
|
_silence_orphan_toctree_warning()
|