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

Adds ability to ignore parse exception console messages #65

Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ parsing_profile
sphinxcontrib_doxylink.egg-info/
dist/
build/
examples/my_lib.tag
7 changes: 7 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ Configuration values
'qtogre_doxygen.pdf': '/home/matt/qtogre/doxygen.pdf',
}
.. confval:: doxylink_parse_error_ignore_regexes

A list of regular expressions that can be used to ignore specific errors reported from the parser.
Default is ``[]``. This is useful if you have a lot of errors that you know are not important.
For example, you may want to ignore errors related to a specific namespace.
The regular expression is matched against the error message using Python's
`re.search <https://docs.python.org/3/library/re.html#re.search>`_ function.

Bug reports
-----------
Expand Down
1 change: 1 addition & 0 deletions examples/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
doxylink = {
'my_lib': (os.path.abspath('./my_lib.tag'), 'https://examples.com/'),
}
doxylink_parse_error_ignore_regexes = [r"DEFINE.*"]

master_doc = 'index'
3 changes: 3 additions & 0 deletions examples/my_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ enum Color { red, green, blue };

// An enum class
enum class Color_c { red, green, blue };

// A function that triggers a warning from the parser
void DEFINE_bool(show, false, "Enable visualization");
3 changes: 2 additions & 1 deletion sphinxcontrib/doxylink/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
__version__ = "1.12.4"


def setup(app):
from .doxylink import setup_doxylink_roles
app.add_config_value('doxylink', {}, 'env')
app.add_config_value('doxylink_pdf_files', {}, 'env')
app.add_config_value('doxylink_parse_error_ignore_regexes',
default=[], types=[str], rebuild='env')
app.connect('builder-inited', setup_doxylink_roles)

return {
Expand Down
36 changes: 28 additions & 8 deletions sphinxcontrib/doxylink/doxylink.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ def is_url(str_to_validate: str) -> bool:

class SymbolMap:
"""A SymbolMap maps symbols to Entries."""
def __init__(self, xml_doc: ET.ElementTree) -> None:
entries = parse_tag_file(xml_doc)
def __init__(self, xml_doc: ET.ElementTree, parse_error_ignore_regexes: Optional[List[str]] = None) -> None:
entries = parse_tag_file(xml_doc, parse_error_ignore_regexes)

# Sort the entry list for use with bisect
self._entries = sorted(entries)
Expand Down Expand Up @@ -225,7 +225,7 @@ def __getitem__(self, item: str) -> Entry:
return self._disambiguate(symbol, candidates)


def parse_tag_file(doc: ET.ElementTree) -> List[Entry]:
def parse_tag_file(doc: ET.ElementTree, parse_error_ignore_regexes: Optional[List[str]]) -> List[Entry]:
"""
Takes in an XML tree from a Doxygen tag file and returns a list that looks something like:

Expand Down Expand Up @@ -290,7 +290,22 @@ def parse_tag_file(doc: ET.ElementTree) -> List[Entry]:
entries.append(
Entry(name=member_symbol, kind=member_kind, file=member_file, arglist=normalised_arglist))
except ParseException as e:
print(f'Skipping {member_kind} {member_symbol}{arglist}. Error reported from parser was: {e}')
message = f'Skipping {member_kind} {member_symbol}{arglist}. Error reported from parser was: {e}'
should_report = True

if parse_error_ignore_regexes:
for pattern in parse_error_ignore_regexes:
try:
if re.search(pattern, message):
should_report = False
break
except re.error:
# Invalid regex pattern - ignore it
continue

if should_report:
report_warning(None, message) # Use None as env since we don't have access to it here
continue
else:
# Put the simple things directly into the list
entries.append(Entry(name=member_symbol, kind=member_kind, file=member_file, arglist=None))
Expand All @@ -303,6 +318,11 @@ def join(*args):


def create_role(app, tag_filename, rootdir, cache_name, pdf=""):
parse_error_ignore_regexes = getattr(app.config, 'doxylink_parse_error_ignore_regexes', [])

if parse_error_ignore_regexes:
report_info(app.env, f'Using parse error ignore patterns: {", ".join(parse_error_ignore_regexes)}')

# Tidy up the root directory path
if not rootdir.endswith(('/', '\\')):
rootdir = join(rootdir, os.sep)
Expand Down Expand Up @@ -330,22 +350,22 @@ def _parse():
if not hasattr(app.env, 'doxylink_cache'):
# no cache present at all, initialise it
report_info(app.env, 'No cache at all, rebuilding...')
mapping = SymbolMap(_parse())
mapping = SymbolMap(_parse(), parse_error_ignore_regexes)
app.env.doxylink_cache = {cache_name: {'mapping': mapping, 'mtime': modification_time, 'version': __version__}}
elif not app.env.doxylink_cache.get(cache_name):
# Main cache is there but the specific sub-cache for this tag file is not
report_info(app.env, 'Sub cache is missing, rebuilding...')
mapping = SymbolMap(_parse())
mapping = SymbolMap(_parse(), parse_error_ignore_regexes)
app.env.doxylink_cache[cache_name] = {'mapping': mapping, 'mtime': modification_time, 'version': __version__}
elif app.env.doxylink_cache[cache_name]['mtime'] < modification_time:
# tag file has been modified since sub-cache creation
report_info(app.env, 'Sub-cache is out of date, rebuilding...')
mapping = SymbolMap(_parse())
mapping = SymbolMap(_parse(), parse_error_ignore_regexes)
app.env.doxylink_cache[cache_name] = {'mapping': mapping, 'mtime': modification_time}
elif not app.env.doxylink_cache[cache_name].get('version') or app.env.doxylink_cache[cache_name].get('version') != __version__:
# sub-cache doesn't have a version or the version doesn't match
report_info(app.env, 'Sub-cache schema version doesn\'t match, rebuilding...')
mapping = SymbolMap(_parse())
mapping = SymbolMap(_parse(), parse_error_ignore_regexes)
app.env.doxylink_cache[cache_name] = {'mapping': mapping, 'mtime': modification_time, 'version': __version__}
else:
# The cache is up to date
Expand Down
59 changes: 58 additions & 1 deletion tests/test_doxylink.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_file_different(examples_tag_file, symbol1, symbol2):

def test_parse_tag_file(examples_tag_file):
tag_file = ET.parse(examples_tag_file)
mapping = doxylink.parse_tag_file(tag_file)
mapping = doxylink.parse_tag_file(tag_file, None)

def has_entry(name):
"""
Expand Down Expand Up @@ -197,3 +197,60 @@ def test_process_configuration_warn(rootdir, pdf_filename, builder, msg):
with LogCapture() as l:
doxylink.process_configuration(app, 'doxygen/project.tag', rootdir, pdf_filename)
l.check(('sphinx.sphinxcontrib.doxylink.doxylink', 'WARNING', msg))


def test_parse_error_ignore_regexes():
# Create a modified tag file content with problematic entries
problematic_xml = """<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<tagfile>
<compound kind="file">
<name>test.h</name>
<filename>test_8h</filename>
<member kind="function">
<name>foo</name>
<arglist>(transform_pb2.Rotation2f a)</arglist>
<anchor>1234</anchor>
</member>
<member kind="function">
<name>bad</name>
<arglist>(*int i)</arglist>
<anchor>5678</anchor>
</member>
<member kind="function">
<name>baz</name>
<arglist>(int i, float f)</arglist>
<anchor>9012</anchor>
</member>
<member kind="function">
<name>unexpected</name>
<arglist>(*int i)</arglist>
<anchor>5678</anchor>
</member>
</compound>
</tagfile>"""

# Write temporary tag file
test_tag_file = 'test_temp.tag'
with open(test_tag_file, 'w') as f:
f.write(problematic_xml)

try:
tag_file = ET.parse(test_tag_file)
patterns = [r'kipping function test\.h::foo', r'kipping.*bad']

with LogCapture() as log:
mapping = doxylink.parse_tag_file(tag_file, patterns)

# Verify that the mapping still contains valid entries
assert any(entry.name.endswith('baz') for entry in mapping)

# Verify that messages matching our patterns were not logged
assert not any('test.h::foo' in record.msg for record in log.records)
assert not any('bar' in record.msg for record in log.records)

# Verify other error messages were logged
assert any('Skipping' in record.msg for record in log.records)

finally:
if os.path.exists(test_tag_file):
os.unlink(test_tag_file)
8 changes: 4 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ envlist = benchmark, test, examples, doc
isolated_build = True

[testenv:benchmark]
whitelist_externals = poetry
allowlist_externals = poetry
commands=
poetry install
python tests/test_parser.py

[testenv:examples]
changedir = examples
whitelist_externals =
allowlist_externals =
doxygen
poetry
commands=
Expand All @@ -19,13 +19,13 @@ commands=
sphinx-build -W -b html . {envtmpdir}/examples/_build

[testenv:test]
whitelist_externals = poetry
allowlist_externals = poetry
commands=
poetry install
pytest

[testenv:doc]
whitelist_externals = poetry
allowlist_externals = poetry
commands=
poetry install
sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees doc {envtmpdir}/linkcheck
Expand Down