Skip to content

Commit f0d8e2e

Browse files
authored
Provide a better error message if the master document is not included in the project documents (#12011)
1 parent e04b0d4 commit f0d8e2e

File tree

3 files changed

+40
-7
lines changed

3 files changed

+40
-7
lines changed

sphinx/builders/__init__.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import codecs
66
import pickle
7+
import re
78
import time
89
from os import path
910
from typing import TYPE_CHECKING, Any, Literal, final
@@ -21,7 +22,7 @@
2122
from sphinx.util.display import progress_message, status_iterator
2223
from sphinx.util.docutils import sphinx_domains
2324
from sphinx.util.i18n import CatalogInfo, CatalogRepository, docname_to_domain
24-
from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath
25+
from sphinx.util.osutil import SEP, canon_path, ensuredir, relative_uri, relpath
2526
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, parallel_available
2627

2728
# side effect: registers roles and directives
@@ -423,9 +424,40 @@ def read(self) -> list[str]:
423424
else:
424425
self._read_serial(docnames)
425426

426-
if self.config.root_doc not in self.env.all_docs:
427-
raise SphinxError('root file %s not found' %
428-
self.env.doc2path(self.config.root_doc))
427+
if self.config.master_doc not in self.env.all_docs:
428+
from sphinx.project import EXCLUDE_PATHS
429+
from sphinx.util.matching import _translate_pattern
430+
431+
master_doc_path = self.env.doc2path(self.config.master_doc)
432+
master_doc_canon = canon_path(master_doc_path)
433+
for pat in EXCLUDE_PATHS:
434+
if not re.match(_translate_pattern(pat), master_doc_canon):
435+
continue
436+
msg = __('Sphinx is unable to load the master document (%s) '
437+
'because it matches a built-in exclude pattern %r. '
438+
'Please move your master document to a different location.')
439+
raise SphinxError(msg % (master_doc_path, pat))
440+
for pat in self.config.exclude_patterns:
441+
if not re.match(_translate_pattern(pat), master_doc_canon):
442+
continue
443+
msg = __('Sphinx is unable to load the master document (%s) '
444+
'because it matches an exclude pattern specified '
445+
'in conf.py, %r. '
446+
'Please remove this pattern from conf.py.')
447+
raise SphinxError(msg % (master_doc_path, pat))
448+
if set(self.config.include_patterns) != {'**'} and not any(
449+
re.match(_translate_pattern(pat), master_doc_canon)
450+
for pat in self.config.include_patterns
451+
):
452+
msg = __('Sphinx is unable to load the master document (%s) '
453+
'because it is not included in the custom include_patterns = %r. '
454+
'Ensure that a pattern in include_patterns matches the '
455+
'master document.')
456+
raise SphinxError(msg % (master_doc_path, self.config.include_patterns))
457+
msg = __('Sphinx is unable to load the master document (%s). '
458+
'The master document must be within the source directory '
459+
'or a subdirectory of it.')
460+
raise SphinxError(msg % master_doc_path)
429461

430462
for retval in self.events.emit('env-updated', self.env):
431463
if retval is not None:

sphinx/config.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,11 +416,12 @@ def __getattr__(self, name: str) -> Any:
416416
except ValueError as exc:
417417
logger.warning("%s", exc)
418418
else:
419-
self.__dict__[name] = value
419+
self.__setattr__(name, value)
420420
return value
421421
# then check values from 'conf.py'
422422
if name in self._raw_config:
423-
self.__dict__[name] = value = self._raw_config[name]
423+
value = self._raw_config[name]
424+
self.__setattr__(name, value)
424425
return value
425426
# finally, fall back to the default value
426427
default = self._options[name].default

tests/test_environment/test_environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_config_status(make_app, app_params):
3434
assert app3.env.config_status == CONFIG_CHANGED
3535
app3.build()
3636
shutil.move(fname[:-4] + 'x.rst', fname)
37-
assert "[config changed ('root_doc')] 1 added" in app3._status.getvalue()
37+
assert "[config changed ('master_doc')] 1 added" in app3._status.getvalue()
3838

3939
# incremental build (extension changed)
4040
app4 = make_app(*args, confoverrides={'extensions': ['sphinx.ext.autodoc']}, **kwargs)

0 commit comments

Comments
 (0)