From 70179078034c99137679f237ad5e5622c1116bc6 Mon Sep 17 00:00:00 2001 From: Lynn-Hu-D Date: Sun, 25 Feb 2024 13:12:12 -0800 Subject: [PATCH 1/4] Provide a better error message if the master document is not included in project documents --- sphinx/builders/__init__.py | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index de25ab06a2d..9af439f286b 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -4,6 +4,7 @@ import codecs import pickle +import re import time from os import path from typing import TYPE_CHECKING, Any, Literal, final @@ -21,7 +22,7 @@ from sphinx.util.display import progress_message, status_iterator from sphinx.util.docutils import sphinx_domains from sphinx.util.i18n import CatalogInfo, CatalogRepository, docname_to_domain -from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath +from sphinx.util.osutil import SEP, canon_path, ensuredir, relative_uri, relpath from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, parallel_available # side effect: registers roles and directives @@ -423,9 +424,40 @@ def read(self) -> list[str]: else: self._read_serial(docnames) - if self.config.root_doc not in self.env.all_docs: - raise SphinxError('root file %s not found' % - self.env.doc2path(self.config.root_doc)) + if self.config.master_doc not in self.env.all_docs: + from sphinx.project import EXCLUDE_PATHS + from sphinx.util.matching import _translate_pattern + + master_doc_path = self.env.doc2path(self.config.master_doc) + master_doc_canon = canon_path(master_doc_path) + for pat in EXCLUDE_PATHS: + if not re.match(_translate_pattern(pat), master_doc_canon): + continue + msg = __('Sphinx is unable to load the master document (%s) ' + 'because it matches a built-in exclude pattern %r. ' + 'Please move your master document to a different location.') + raise SphinxError(msg % (master_doc_path, pat)) + for pat in self.config.exclude_patterns: + if not re.match(_translate_pattern(pat), master_doc_canon): + continue + msg = __('Sphinx is unable to load the master document (%s) ' + 'because it matches an exclude pattern specified ' + 'in conf.py, %r. ' + 'Please remove this pattern from conf.py.') + raise SphinxError(msg % (master_doc_path, pat)) + if set(self.config.include_patterns) != {'**'} and not any( + re.match(_translate_pattern(pat), master_doc_canon) + for pat in self.config.include_patterns + ): + msg = __('Sphinx is unable to load the master document (%s) ' + 'because it is not included in the custom include_patterns = %r. ' + 'Ensure that a pattern in include_patterns matches the ' + 'master document.') + raise SphinxError(msg % (master_doc_path, self.config.include_patterns)) + msg = __('Sphinx is unable to load the master document (%s). ' + 'The master document must be within the source directory ' + 'or a subdirectory of it.') + raise SphinxError(msg % master_doc_path) for retval in self.events.emit('env-updated', self.env): if retval is not None: From 8a613f13ccfc7f572ce9d5d54b709dcc3199686f Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sun, 14 Jul 2024 02:31:28 +0100 Subject: [PATCH 2/4] setattr --- sphinx/config.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 24984ac6682..b6d7c1aff68 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -409,18 +409,19 @@ def __getattr__(self, name: str) -> Any: if name in self._overrides: value = self._overrides[name] if not isinstance(value, str): - self.__dict__[name] = value + self.__setattr__(name, value) return value try: value = self.convert_overrides(name, value) except ValueError as exc: logger.warning("%s", exc) else: - self.__dict__[name] = value + self.__setattr__(name, value) return value # then check values from 'conf.py' if name in self._raw_config: - self.__dict__[name] = value = self._raw_config[name] + value = self._raw_config[name] + self.__setattr__(name, value) return value # finally, fall back to the default value default = self._options[name].default From 594715dd777740da060be8721734fb8ca6050454 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sun, 14 Jul 2024 02:55:30 +0100 Subject: [PATCH 3/4] setattr --- sphinx/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index b6d7c1aff68..88256b6cb81 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -409,14 +409,14 @@ def __getattr__(self, name: str) -> Any: if name in self._overrides: value = self._overrides[name] if not isinstance(value, str): - self.__setattr__(name, value) + self.__dict__[name] = value return value try: value = self.convert_overrides(name, value) except ValueError as exc: logger.warning("%s", exc) else: - self.__setattr__(name, value) + self.__dict__[name] = value return value # then check values from 'conf.py' if name in self._raw_config: From 80528e00b93ec0e8025e380dbc9fb0d7e8304152 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sun, 14 Jul 2024 03:15:53 +0100 Subject: [PATCH 4/4] setattr --- sphinx/config.py | 2 +- tests/test_environment/test_environment.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 88256b6cb81..008cc1cd0ef 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -416,7 +416,7 @@ def __getattr__(self, name: str) -> Any: except ValueError as exc: logger.warning("%s", exc) else: - self.__dict__[name] = value + self.__setattr__(name, value) return value # then check values from 'conf.py' if name in self._raw_config: diff --git a/tests/test_environment/test_environment.py b/tests/test_environment/test_environment.py index 8a34457cb90..82de5975877 100644 --- a/tests/test_environment/test_environment.py +++ b/tests/test_environment/test_environment.py @@ -34,7 +34,7 @@ def test_config_status(make_app, app_params): assert app3.env.config_status == CONFIG_CHANGED app3.build() shutil.move(fname[:-4] + 'x.rst', fname) - assert "[config changed ('root_doc')] 1 added" in app3._status.getvalue() + assert "[config changed ('master_doc')] 1 added" in app3._status.getvalue() # incremental build (extension changed) app4 = make_app(*args, confoverrides={'extensions': ['sphinx.ext.autodoc']}, **kwargs)