Skip to content
Open
Show file tree
Hide file tree
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
30 changes: 30 additions & 0 deletions sphinxarg/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,36 @@ class ArgParseDomain(Domain):
def get_objects(self) -> Iterable[_ObjectDescriptionTuple]:
yield from self.data['commands']

def clear_doc(self, docname: str) -> None:
"""Remove traces of a document in the domain-specific inventories."""
self.data['commands'] = [
entry for entry in self.data['commands'] if entry[3] != docname
]
commands_by_group: dict[str, list[_ObjectDescriptionTuple]]
commands_by_group = self.data['commands-by-group']
for group in list(commands_by_group):
entries = [e for e in commands_by_group[group] if e[3] != docname]
if entries:
commands_by_group[group] = entries
else:
del commands_by_group[group]

def merge_domaindata(self, docnames: Iterable[str], otherdata: dict) -> None:
"""Merge in data regarding *docnames* from a different domaindata
inventory (coming from a subprocess in parallel builds).
"""
docnames = set(docnames)
# No need to check for duplicates: each docname is only ever merged
# once, from the single worker process that read it.
for entry in otherdata['commands']:
if entry[3] in docnames:
self.data['commands'].append(entry)
for group, entries in otherdata['commands-by-group'].items():
merged = self.data['commands-by-group'].setdefault(group, [])
for entry in entries:
if entry[3] in docnames:
merged.append(entry)

def resolve_xref(
self,
env: BuildEnvironment,
Expand Down
2 changes: 2 additions & 0 deletions test/roots/test-parallel-build/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extensions = ['sphinxarg.ext']
sphinxarg_build_commands_index = True
11 changes: 11 additions & 0 deletions test/roots/test-parallel-build/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Test Parallel Build
====================

.. toctree::

page1
page2
page3
page4
page5
page6
8 changes: 8 additions & 0 deletions test/roots/test-parallel-build/page1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Page 1
======

.. argparse::
:filename: sample-directive-opts.py
:func: get_parser
:nosubcommands:
:prog: page1-command
8 changes: 8 additions & 0 deletions test/roots/test-parallel-build/page2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Page 2
======

.. argparse::
:filename: sample-directive-opts.py
:func: get_parser
:nosubcommands:
:prog: page2-command
8 changes: 8 additions & 0 deletions test/roots/test-parallel-build/page3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Page 3
======

.. argparse::
:filename: sample-directive-opts.py
:func: get_parser
:nosubcommands:
:prog: page3-command
8 changes: 8 additions & 0 deletions test/roots/test-parallel-build/page4.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Page 4
======

.. argparse::
:filename: sample-directive-opts.py
:func: get_parser
:nosubcommands:
:prog: page4-command
8 changes: 8 additions & 0 deletions test/roots/test-parallel-build/page5.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Page 5
======

.. argparse::
:filename: sample-directive-opts.py
:func: get_parser
:nosubcommands:
:prog: page5-command
8 changes: 8 additions & 0 deletions test/roots/test-parallel-build/page6.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Page 6
======

.. argparse::
:filename: sample-directive-opts.py
:func: get_parser
:nosubcommands:
:prog: page6-command
28 changes: 28 additions & 0 deletions test/test_parallel_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Regression test for parallel reads (see issue with merge_domaindata).

Sphinx only performs a parallel *read* when there are more than 5 source
documents and ``parallel > 1`` (see ``Builder.read`` in sphinx/builders/__init__.py).
The test root therefore needs at least 6 documents with ``argparse``
directives so that reading is actually split across worker processes and
``ArgParseDomain.merge_domaindata`` is exercised.
"""

import pytest

from sphinxarg.ext import ArgParseDomain


@pytest.mark.sphinx('dummy', testroot='parallel-build', parallel=2)
def test_parallel_read_merges_domain_data(app):
# Building must not raise NotImplementedError / KeyError from
# sphinx.util.parallel when merging results from worker processes.
app.build()
assert app._warning.getvalue() == ''

domain = app.env.domains[ArgParseDomain.name]
full_commands = {entry[0] for entry in domain.get_objects()}
expected = {f'page{i}-command' for i in range(1, 7)}
assert full_commands == expected

# Each command should only be merged in once, from the worker that read it.
assert len(list(domain.get_objects())) == len(expected)
Loading