From d80eab689d880e5052a93d43f6fafb637025bb2d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sun, 13 Aug 2023 20:07:28 +0100 Subject: [PATCH] Fix EM10{1,2} (exception must not use a string) --- pyproject.toml | 3 --- sphinx/addnodes.py | 3 ++- sphinx/application.py | 12 ++++++++---- sphinx/builders/epub3.py | 3 ++- sphinx/builders/gettext.py | 3 ++- sphinx/builders/html/__init__.py | 3 ++- sphinx/builders/html/_assets.py | 12 ++++++++---- sphinx/deprecation.py | 6 ++++-- sphinx/directives/__init__.py | 3 ++- sphinx/domains/c.py | 6 ++++-- sphinx/domains/cpp.py | 12 ++++++++---- sphinx/domains/python.py | 21 ++++++++++++--------- sphinx/environment/adapters/toctree.py | 6 ++++-- sphinx/ext/autodoc/__init__.py | 9 ++++++--- sphinx/ext/autosummary/__init__.py | 2 +- sphinx/ext/autosummary/generate.py | 6 ++++-- sphinx/ext/imgmath.py | 9 ++++++--- sphinx/ext/inheritance_diagram.py | 4 ++-- sphinx/ext/intersphinx.py | 3 ++- sphinx/ext/linkcode.py | 4 ++-- sphinx/ext/mathjax.py | 4 ++-- sphinx/pycode/__init__.py | 3 ++- sphinx/registry.py | 3 ++- sphinx/testing/fixtures.py | 10 +++++----- sphinx/testing/util.py | 4 ++-- sphinx/transforms/__init__.py | 3 ++- sphinx/transforms/i18n.py | 5 +++-- sphinx/transforms/post_transforms/images.py | 3 ++- sphinx/util/__init__.py | 6 ++++-- sphinx/util/docutils.py | 6 ++++-- sphinx/util/i18n.py | 3 ++- sphinx/util/images.py | 3 ++- sphinx/util/index_entries.py | 6 ++++-- sphinx/util/nodes.py | 6 ++++-- sphinx/util/osutil.py | 7 ++++--- sphinx/util/tags.py | 6 ++++-- sphinx/util/typing.py | 8 +++++--- sphinx/writers/text.py | 9 ++++++--- tests/test_build_epub.py | 3 ++- tests/test_build_gettext.py | 6 ++++-- tests/test_build_html.py | 3 ++- tests/test_build_latex.py | 4 ++-- tests/test_build_texinfo.py | 3 ++- tests/test_domain_c.py | 12 ++++++------ tests/test_domain_cpp.py | 12 ++++++------ tests/test_ext_math.py | 18 ++++++++++++------ tests/test_util_display.py | 2 +- utils/bump_version.py | 3 ++- 48 files changed, 179 insertions(+), 112 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6fc07cc91..090040267 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -170,9 +170,6 @@ ignore = [ "D", # flake8-django "DJ", # Django is not used in Sphinx - # flake8-errmsg - "EM101", # exception must not use a string literal, assign to variable first - "EM102", # exception must not use an f-string literal, assign to variable first # eradicate "ERA001", # found commented-out code # flake8-executable diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index c65b8369b..e8bbd36ac 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -21,7 +21,8 @@ _DEPRECATED_OBJECTS = { def __getattr__(name): if name not in _DEPRECATED_OBJECTS: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) from sphinx.deprecation import _deprecation_warning diff --git a/sphinx/application.py b/sphinx/application.py index c62714375..8f1dec100 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -423,7 +423,8 @@ class Sphinx: else: major, minor = map(int, version.split('.')[:2]) if (major, minor) > sphinx.version_info[:2]: - raise VersionRequirementError(f'{major}.{minor}') + req = f'{major}.{minor}' + raise VersionRequirementError(req) # event interface def connect(self, event: str, callback: Callable, priority: int = 500) -> int: @@ -1336,7 +1337,8 @@ class TemplateBridge: *theme* is a :class:`sphinx.theming.Theme` object or None; in the latter case, *dirs* can be list of fixed directories to look for templates. """ - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) def newest_template_mtime(self) -> float: """Called by the builder to determine if output files are outdated @@ -1349,10 +1351,12 @@ class TemplateBridge: """Called by the builder to render a template given as a filename with a specified context (a Python dictionary). """ - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) def render_string(self, template: str, context: dict) -> str: """Called by the builder to render a template given as a string with a specified context (a Python dictionary). """ - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index 24ba13215..d93d79d66 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -162,7 +162,8 @@ class Epub3Builder(_epub_base.EpubBuilder): navstack[-1].children.append(navpoint) navstack.append(navpoint) else: - raise RuntimeError('Should never reach here. It might be a bug.') + unreachable = 'Should never reach here. It might be a bug.' + raise RuntimeError(unreachable) return navstack[0].children diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 660152af5..04ef9e463 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -245,7 +245,8 @@ class MessageCatalogBuilder(I18nBuilder): origin = MsgOrigin(template, line) self.catalogs['sphinx'].add(msg, origin) except Exception as exc: - raise ThemeError(f'{template}: {exc!r}') from exc + msg = f'{template}: {exc!r}' + raise ThemeError(msg) from exc def build( self, diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 425d7bfa1..54aeff496 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -1381,7 +1381,8 @@ _DEPRECATED_OBJECTS = { def __getattr__(name): if name not in _DEPRECATED_OBJECTS: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) from sphinx.deprecation import _deprecation_warning diff --git a/sphinx/builders/html/_assets.py b/sphinx/builders/html/_assets.py index 9ace27a10..e7e72709a 100644 --- a/sphinx/builders/html/_assets.py +++ b/sphinx/builders/html/_assets.py @@ -44,10 +44,12 @@ class _CascadingStyleSheet: return hash((self.filename, self.priority, *sorted(self.attributes.items()))) def __setattr__(self, key, value): - raise AttributeError(f'{self.__class__.__name__} is immutable') + msg = f'{self.__class__.__name__} is immutable' + raise AttributeError(msg) def __delattr__(self, key): - raise AttributeError(f'{self.__class__.__name__} is immutable') + msg = f'{self.__class__.__name__} is immutable' + raise AttributeError(msg) class _JavaScript: @@ -84,10 +86,12 @@ class _JavaScript: return hash((self.filename, self.priority, *sorted(self.attributes.items()))) def __setattr__(self, key, value): - raise AttributeError(f'{self.__class__.__name__} is immutable') + msg = f'{self.__class__.__name__} is immutable' + raise AttributeError(msg) def __delattr__(self, key): - raise AttributeError(f'{self.__class__.__name__} is immutable') + msg = f'{self.__class__.__name__} is immutable' + raise AttributeError(msg) def _file_checksum(outdir: Path, filename: str | os.PathLike[str]) -> str: diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index d708fdaa0..8a242d7da 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -37,7 +37,8 @@ def _deprecation_warning( def __getattr__(name): if name not in _DEPRECATED_OBJECTS: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) from sphinx.deprecation import _deprecation_warning @@ -51,7 +52,8 @@ def _deprecation_warning( elif remove == (9, 0): warning_class = RemovedInSphinx90Warning else: - raise RuntimeError(f'removal version {remove!r} is invalid!') + msg = f'removal version {remove!r} is invalid!' + raise RuntimeError(msg) qualified_name = f'{module}.{attribute}' if canonical_name: diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 1f8fa90bb..c2ed1a672 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -35,7 +35,8 @@ def optional_int(argument: str) -> int | None: else: value = int(argument) if value < 0: - raise ValueError('negative value; must be positive or zero') + msg = 'negative value; must be positive or zero' + raise ValueError(msg) return value diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index cf6ac5137..f45ee014a 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -214,11 +214,13 @@ class ASTNestedName(ASTBase): # just print the name part, with template args, not template params if mode == 'noneIsName': if self.rooted: - raise AssertionError("Can this happen?") # TODO + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO signode += nodes.Text('.') for i in range(len(self.names)): if i != 0: - raise AssertionError("Can this happen?") # TODO + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO signode += nodes.Text('.') n = self.names[i] n.describe_signature(signode, mode, env, '', symbol) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index a85f55014..39a346644 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -775,15 +775,18 @@ class ASTNestedName(ASTBase): # just print the name part, with template args, not template params if mode == 'noneIsName': if self.rooted: - raise AssertionError("Can this happen?") # TODO + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO signode += nodes.Text('::') for i in range(len(self.names)): if i != 0: - raise AssertionError("Can this happen?") # TODO + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO signode += nodes.Text('::blah') n = self.names[i] if self.templates[i]: - raise AssertionError("Can this happen?") # TODO + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO signode += nodes.Text("template") signode += nodes.Text(" ") n.describe_signature(signode, mode, env, '', symbol) @@ -6120,7 +6123,8 @@ class DefinitionParser(BaseParser): if modifier is not None: self.fail(f"Can not have {modifier} without a floating point type.") else: - raise AssertionError(f"Unhandled type {typ}") + msg = f'Unhandled type {typ}' + raise AssertionError(msg) canonNames: list[str] = [] if modifier is not None: diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index ded18c921..4c79eba21 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -310,8 +310,9 @@ class _TypeParameterListParser(TokenProcessor): tp_default = self._build_identifier(tokens) if tp_kind != Parameter.POSITIONAL_OR_KEYWORD and tp_ann != Parameter.empty: - raise SyntaxError('type parameter bound or constraint is not allowed ' - f'for {tp_kind.description} parameters') + msg = ('type parameter bound or constraint is not allowed ' + f'for {tp_kind.description} parameters') + raise SyntaxError(msg) type_param = (tp_name, tp_kind, tp_default, tp_ann) self.type_params.append(type_param) @@ -408,8 +409,9 @@ def _parse_type_list( for (tp_name, tp_kind, tp_default, tp_ann) in parser.type_params: # no positional-only or keyword-only allowed in a type parameters list if tp_kind in {Parameter.POSITIONAL_ONLY, Parameter.KEYWORD_ONLY}: - raise SyntaxError('positional-only or keyword-only parameters' - ' are prohibited in type parameter lists') + msg = ('positional-only or keyword-only parameters ' + 'are prohibited in type parameter lists') + raise SyntaxError(msg) node = addnodes.desc_type_parameter() if tp_kind == Parameter.VAR_POSITIONAL: @@ -774,10 +776,10 @@ class PyObject(ObjectDescription[tuple[str, str]]): sig_prefix = self.get_signature_prefix(sig) if sig_prefix: if type(sig_prefix) is str: - raise TypeError( - "Python directive method get_signature_prefix()" - " must return a list of nodes." - f" Return value was '{sig_prefix}'.") + msg = ("Python directive method get_signature_prefix()" + " must return a list of nodes." + f" Return value was '{sig_prefix}'.") + raise TypeError(msg) signode += addnodes.desc_annotation(str(sig_prefix), '', *sig_prefix) if prefix: @@ -839,7 +841,8 @@ class PyObject(ObjectDescription[tuple[str, str]]): def get_index_text(self, modname: str, name: tuple[str, str]) -> str: """Return the text for the index entry of the object.""" - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) def add_target_and_index(self, name_cls: tuple[str, str], sig: str, signode: desc_signature) -> None: diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py index cbd71f56c..1626610ab 100644 --- a/sphinx/environment/adapters/toctree.py +++ b/sphinx/environment/adapters/toctree.py @@ -297,7 +297,8 @@ def _toctree_entry( 'detected, ignoring: %s <- %s'), ref, ' <- '.join(parents), location=ref, type='toc', subtype='circular') - raise LookupError('circular reference') + msg = 'circular reference' + raise LookupError(msg) toc, refdoc = _toctree_standard_entry( title, @@ -468,7 +469,8 @@ def _toctree_copy(node: ET, depth: int, maxdepth: int, collapse: bool, tags: Tag child.parent = sub_node_copy copy.append(sub_node_copy) else: - raise ValueError(f"Unexpected node type {subnode.__class__.__name__!r}!") + msg = f'Unexpected node type {subnode.__class__.__name__!r}!' + raise ValueError(msg) return copy diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index de80dc9c1..d13327c97 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -320,7 +320,8 @@ class Documenter: def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: """Called to see if a member can be documented by this Documenter.""" - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) def __init__(self, directive: DocumenterBridge, name: str, indent: str = '') -> None: self.directive = directive @@ -368,7 +369,8 @@ class Documenter: example, it would return ``('zipfile', ['ZipFile', 'open'])`` for the ``zipfile.ZipFile.open`` method. """ - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) def parse_name(self) -> bool: """Determine what module to import and what attribute to document. @@ -626,7 +628,8 @@ class Documenter: If *want_all* is True, return all members. Else, only return those members given by *self.options.members* (which may also be None). """ - raise NotImplementedError('must be implemented in subclasses') + msg = 'must be implemented in subclasses' + raise NotImplementedError(msg) def filter_members(self, members: ObjectMembers, want_all: bool, ) -> list[tuple[str, Any, bool]]: diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 64450593e..059923adb 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -695,7 +695,7 @@ def _import_by_name(name: str, grouped_exception: bool = True) -> tuple[Any, Any except (ValueError, ImportError, AttributeError, KeyError) as exc: errors.append(exc) if grouped_exception: - raise ImportExceptionGroup('', errors) from None + raise ImportExceptionGroup('', errors) from None # NoQA: EM101 else: raise ImportError(*exc.args) from exc diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index e808cb00e..abffcef49 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -109,7 +109,8 @@ def setup_documenters(app: Any) -> None: def _underline(title: str, line: str = '=') -> str: if '\n' in title: - raise ValueError('Can only underline single lines') + msg = 'Can only underline single lines' + raise ValueError(msg) return title + '\n' + line * len(title) @@ -118,7 +119,8 @@ class AutosummaryRenderer: def __init__(self, app: Sphinx) -> None: if isinstance(app, Builder): - raise ValueError('Expected a Sphinx application object!') + msg = 'Expected a Sphinx application object!' + raise ValueError(msg) system_templates_path = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')] loader = SphinxTemplateLoader(app.srcdir, app.config.templates_path, diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index 448b33fcb..f192d256f 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -156,7 +156,8 @@ def compile_math(latex: str, builder: Builder) -> str: builder.config.imgmath_latex) raise InvokeError from exc except CalledProcessError as exc: - raise MathExtError('latex exited with error', exc.stderr, exc.stdout) from exc + msg = 'latex exited with error' + raise MathExtError(msg, exc.stderr, exc.stdout) from exc def convert_dvi_to_image(command: list[str], name: str) -> tuple[str, str]: @@ -236,7 +237,8 @@ def render_math( """ image_format = self.builder.config.imgmath_image_format.lower() if image_format not in SUPPORT_FORMAT: - raise MathExtError('imgmath_image_format must be either "png" or "svg"') + unsupported_format_msg = 'imgmath_image_format must be either "png" or "svg"' + raise MathExtError(unsupported_format_msg) latex = generate_latex_macro(image_format, math, @@ -285,7 +287,8 @@ def render_maths_to_base64(image_format: str, generated_path: str) -> str: return f'data:image/png;base64,{encoded}' if image_format == 'svg': return f'data:image/svg+xml;base64,{encoded}' - raise MathExtError('imgmath_image_format must be either "png" or "svg"') + unsupported_format_msg = 'imgmath_image_format must be either "png" or "svg"' + raise MathExtError(unsupported_format_msg) def clean_up_files(app: Sphinx, exc: Exception) -> None: diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index 92af13319..ab653faad 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -152,8 +152,8 @@ class InheritanceGraph: self.class_info = self._class_info(classes, show_builtins, private_bases, parts, aliases, top_classes) if not self.class_info: - raise InheritanceException('No classes found for ' - 'inheritance diagram') + msg = 'No classes found for inheritance diagram' + raise InheritanceException(msg) def _import_classes(self, class_names: list[str], currmodule: str) -> list[Any]: """Import a list of classes.""" diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index f56d4e794..6a612f2ab 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -566,7 +566,8 @@ class IntersphinxRole(SphinxRole): elif name[8] == ':': return None, suffix else: - raise ValueError(f'Malformed :external: role name: {name}') + msg = f'Malformed :external: role name: {name}' + raise ValueError(msg) def get_role_name(self, name: str) -> tuple[str, str] | None: names = name.split(':') diff --git a/sphinx/ext/linkcode.py b/sphinx/ext/linkcode.py index 259a5d390..9c900d5cd 100644 --- a/sphinx/ext/linkcode.py +++ b/sphinx/ext/linkcode.py @@ -23,8 +23,8 @@ def doctree_read(app: Sphinx, doctree: Node) -> None: resolve_target = getattr(env.config, 'linkcode_resolve', None) if not callable(env.config.linkcode_resolve): - raise LinkcodeError( - "Function `linkcode_resolve` is not given in conf.py") + msg = 'Function `linkcode_resolve` is not given in conf.py' + raise LinkcodeError(msg) assert resolve_target is not None # for mypy domain_keys = { diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py index 0131a9441..bf2db7356 100644 --- a/sphinx/ext/mathjax.py +++ b/sphinx/ext/mathjax.py @@ -76,8 +76,8 @@ def install_mathjax(app: Sphinx, pagename: str, templatename: str, context: dict ): return if not app.config.mathjax_path: - raise ExtensionError('mathjax_path config value must be set for the ' - 'mathjax extension to work') + msg = 'mathjax_path config value must be set for the mathjax extension to work' + raise ExtensionError(msg) domain = cast(MathDomain, app.env.get_domain('math')) builder = cast(StandaloneHTMLBuilder, app.builder) diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index db9f28bab..55835ecec 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -138,7 +138,8 @@ class ModuleAnalyzer: self.tagorder = parser.deforders self._analyzed = True except Exception as exc: - raise PycodeError(f'parsing {self.srcname!r} failed: {exc!r}') from exc + msg = f'parsing {self.srcname!r} failed: {exc!r}' + raise PycodeError(msg) from exc def find_attr_docs(self) -> dict[tuple[str, str], list[str]]: """Find class and module-level attributes and their documentation.""" diff --git a/sphinx/registry.py b/sphinx/registry.py index d2f5f6c9e..1a3f9fadf 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -336,7 +336,8 @@ class SphinxComponentRegistry: try: return builder.default_translator_class except AttributeError as err: - raise AttributeError(f'translator not found for {builder.name}') from err + msg = f'translator not found for {builder.name}' + raise AttributeError(msg) from err def create_translator(self, builder: Builder, *args: Any) -> nodes.NodeVisitor: translator_class = self.get_translator_class(builder) diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py index 8f9ae89e9..0cc4882fe 100644 --- a/sphinx/testing/fixtures.py +++ b/sphinx/testing/fixtures.py @@ -83,8 +83,8 @@ def app_params(request: Any, test_params: dict, shared_result: SharedResult, # ##### process pytest.mark.test_params if test_params['shared_result']: if 'srcdir' in kwargs: - raise pytest.Exception('You can not specify shared_result and ' - 'srcdir in same time.') + msg = 'You can not specify shared_result and srcdir in same time.' + raise pytest.Exception(msg) kwargs['srcdir'] = test_params['shared_result'] restore = shared_result.restore(test_params['shared_result']) kwargs.update(restore) @@ -123,9 +123,9 @@ def test_params(request: Any) -> dict: } result.update(kwargs) - if (result['shared_result'] and not isinstance(result['shared_result'], str)): - raise pytest.Exception('You can only provide a string type of value ' - 'for "shared_result" ') + if result['shared_result'] and not isinstance(result['shared_result'], str): + msg = 'You can only provide a string type of value for "shared_result"' + raise pytest.Exception(msg) return result diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py index 86b66bf57..3d73b6ee9 100644 --- a/sphinx/testing/util.py +++ b/sphinx/testing/util.py @@ -56,8 +56,8 @@ def assert_node(node: Node, cls: Any = None, xpath: str = "", **kwargs: Any) -> for key, value in kwargs.items(): if key not in node: if (key := key.replace('_', '-')) not in node: - raise AssertionError(f'The node{xpath} does not have {key!r}' - f' attribute: {node!r}') + msg = f'The node{xpath} does not have {key!r} attribute: {node!r}' + raise AssertionError(msg) assert node[key] == value, \ f'The node{xpath}[{key}] is not {value!r}: {node[key]!r}' diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index 3d6c447e3..c6a280652 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -482,7 +482,8 @@ def _sort_key(node: nodes.Node) -> int: return 0 if isinstance(node, nodes.target): return 1 - raise ValueError(f'_sort_key called with unexpected node type {type(node)!r}') + msg = f'_sort_key called with unexpected node type {type(node)!r}' + raise ValueError(msg) def setup(app: Sphinx) -> dict[str, Any]: diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 5044d5726..e215c13e8 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -580,8 +580,9 @@ class AddTranslationClasses(SphinxTransform): add_translated = False add_untranslated = True else: - raise ConfigError('translation_progress_classes must be' - ' True, False, "translated" or "untranslated"') + msg = ('translation_progress_classes must be ' + 'True, False, "translated" or "untranslated"') + raise ConfigError(msg) for node in self.document.findall(NodeMatcher(translated=Any)): # type: nodes.Element if node['translated']: diff --git a/sphinx/transforms/post_transforms/images.py b/sphinx/transforms/post_transforms/images.py index e1c7fe11b..7ce6be8e9 100644 --- a/sphinx/transforms/post_transforms/images.py +++ b/sphinx/transforms/post_transforms/images.py @@ -218,7 +218,8 @@ class ImageConverter(BaseImageConverter): if rule in self.conversion_rules: return rule - raise ValueError('No conversion rule found') + msg = 'No conversion rule found' + raise ValueError(msg) def is_available(self) -> bool: """Return the image converter is available or not.""" diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 18d8519cc..9970614da 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -215,7 +215,8 @@ def parselinenos(spec: str, total: int) -> list[int]: else: raise ValueError except ValueError as exc: - raise ValueError(f'invalid line number spec: {spec!r}') from exc + msg = f'invalid line number spec: {spec!r}' + raise ValueError(msg) from exc return items @@ -286,7 +287,8 @@ _DEPRECATED_OBJECTS = { def __getattr__(name): if name not in _DEPRECATED_OBJECTS: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) from sphinx.deprecation import _deprecation_warning diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index c6ff67516..993d1fa3b 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -46,7 +46,8 @@ _DEPRECATED_OBJECTS = { def __getattr__(name): if name not in _DEPRECATED_OBJECTS: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) from sphinx.deprecation import _deprecation_warning @@ -478,7 +479,8 @@ class SphinxRole: if not self.name: self.name = self.env.config.default_role if not self.name: - raise SphinxError('cannot determine default role!') + msg = 'cannot determine default role!' + raise SphinxError(msg) return self.run() diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index 1c69eb13f..b82088470 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -240,7 +240,8 @@ def get_image_filename_for_language( language=env.config.language, ) except KeyError as exc: - raise SphinxError(f'Invalid figure_language_filename: {exc!r}') from exc + msg = f'Invalid figure_language_filename: {exc!r}' + raise SphinxError(msg) from exc def search_image_for_language(filename: str, env: BuildEnvironment) -> str: diff --git a/sphinx/util/images.py b/sphinx/util/images.py index f9bac6e56..ac0e7f42f 100644 --- a/sphinx/util/images.py +++ b/sphinx/util/images.py @@ -142,4 +142,5 @@ def _image_type_from_file(filename: PathLike[str] | str) -> str: if header.startswith(b'RIFF') and header[8:12] == b'WEBP': return 'webp' - raise ValueError('Could not detect image type!') + msg = 'Could not detect image type!' + raise ValueError(msg) diff --git a/sphinx/util/index_entries.py b/sphinx/util/index_entries.py index fb2d15a4d..100468429 100644 --- a/sphinx/util/index_entries.py +++ b/sphinx/util/index_entries.py @@ -14,12 +14,14 @@ def split_index_msg(entry_type: str, value: str) -> list[str]: return _split_into(3, 'triple', value) if entry_type in {'see', 'seealso'}: return _split_into(2, 'see', value) - raise ValueError(f'invalid {entry_type} index entry {value!r}') + msg = f'invalid {entry_type} index entry {value!r}' + raise ValueError(msg) def _split_into(n: int, type: str, value: str) -> list[str]: """Split an index entry into a given number of parts at semicolons.""" parts = [x.strip() for x in value.split(';', n - 1)] if len(list(filter(None, parts))) < n: - raise ValueError(f'invalid {type} index entry {value!r}') + msg = f'invalid {type} index entry {value!r}' + raise ValueError(msg) return parts diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index bdfeed008..4ef9db4bd 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -275,14 +275,16 @@ def get_node_source(node: Element) -> str: for pnode in traverse_parent(node): if pnode.source: return pnode.source - raise ValueError("node source not found") + msg = 'node source not found' + raise ValueError(msg) def get_node_line(node: Element) -> int: for pnode in traverse_parent(node): if pnode.line: return pnode.line - raise ValueError("node line not found") + msg = 'node line not found' + raise ValueError(msg) def traverse_parent(node: Element, cls: Any = None) -> Iterable[Element]: diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 94f975b64..58fbac730 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -179,7 +179,8 @@ class FileAvoidWrite: def close(self) -> None: """Stop accepting writes and write file, if needed.""" if not self._io: - raise Exception('FileAvoidWrite does not support empty files.') + msg = 'FileAvoidWrite does not support empty files.' + raise Exception(msg) buf = self.getvalue() self._io.close() @@ -207,8 +208,8 @@ class FileAvoidWrite: def __getattr__(self, name: str) -> Any: # Proxy to _io instance. if not self._io: - raise Exception('Must write to FileAvoidWrite before other ' - 'methods can be used') + msg = 'Must write to FileAvoidWrite before other methods can be used' + raise Exception(msg) return getattr(self._io, name) diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py index 40f9bcde5..ad05aafa7 100644 --- a/sphinx/util/tags.py +++ b/sphinx/util/tags.py @@ -63,7 +63,8 @@ class Tags: parser = BooleanParser(env, condition, state='variable') expr = parser.parse_expression() if not parser.stream.eos: - raise ValueError('chunk after expression') + msg = 'chunk after expression' + raise ValueError(msg) def eval_node(node: Node) -> bool: if isinstance(node, nodes.CondExpr): @@ -80,6 +81,7 @@ class Tags: elif isinstance(node, nodes.Name): return self.tags.get(node.name, False) else: - raise ValueError('invalid node, check parsing') + msg = 'invalid node, check parsing' + raise ValueError(msg) return eval_node(expr) diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 8d90bc4e8..0a14c99b1 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -232,8 +232,9 @@ def stringify_annotation( from sphinx.util.inspect import isNewType # lazy loading if mode not in {'fully-qualified-except-typing', 'fully-qualified', 'smart'}: - raise ValueError("'mode' must be one of 'fully-qualified-except-typing', " - f"'fully-qualified', or 'smart'; got {mode!r}.") + msg = ("'mode' must be one of 'fully-qualified-except-typing', " + f"'fully-qualified', or 'smart'; got {mode!r}.") + raise ValueError(msg) if mode == 'smart': module_prefix = '~' @@ -352,7 +353,8 @@ _DEPRECATED_OBJECTS = { def __getattr__(name): if name not in _DEPRECATED_OBJECTS: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) from sphinx.deprecation import _deprecation_warning diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index a49c61845..492b917fc 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -155,7 +155,8 @@ class Table: This takes into account cells spanning multiple columns. """ if cell.row is None or cell.col is None: - raise ValueError('Cell co-ordinates have not been set') + msg = 'Cell co-ordinates have not been set' + raise ValueError(msg) width = 0 for i in range(self[cell.row, cell.col].colspan): width += source[cell.col + i] @@ -180,7 +181,8 @@ class Table: if not cell.wrapped: continue if cell.row is None or cell.col is None: - raise ValueError('Cell co-ordinates have not been set') + msg = 'Cell co-ordinates have not been set' + raise ValueError(msg) width = math.ceil(max(column_width(x) for x in cell.wrapped) / cell.colspan) for col in range(cell.col, cell.col + cell.colspan): self.measured_widths[col] = max(self.measured_widths[col], width) @@ -891,7 +893,8 @@ class TextTranslator(SphinxTranslator): def visit_table(self, node: Element) -> None: if hasattr(self, 'table'): - raise NotImplementedError('Nested tables are not supported.') + msg = 'Nested tables are not supported.' + raise NotImplementedError(msg) self.new_state(0) self.table = Table() diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index ba4741013..7f5b8154f 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -385,7 +385,8 @@ def test_run_epubcheck(app): except CalledProcessError as exc: print(exc.stdout.decode('utf-8')) print(exc.stderr.decode('utf-8')) - raise AssertionError(f'epubcheck exited with return code {exc.returncode}') from exc + msg = f'epubcheck exited with return code {exc.returncode}' + raise AssertionError(msg) from exc def test_xml_name_pattern_check(): diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 6424f8dc0..6d9154e68 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -74,7 +74,8 @@ def test_msgfmt(app): except CalledProcessError as exc: print(exc.stdout) print(exc.stderr) - raise AssertionError(f'msginit exited with return code {exc.returncode}') from exc + msg = f'msginit exited with return code {exc.returncode}' + raise AssertionError(msg) from exc assert (app.outdir / 'en_US.po').is_file(), 'msginit failed' try: @@ -86,7 +87,8 @@ def test_msgfmt(app): except CalledProcessError as exc: print(exc.stdout) print(exc.stderr) - raise AssertionError(f'msgfmt exited with return code {exc.returncode}') from exc + msg = f'msgfmt exited with return code {exc.returncode}' + raise AssertionError(msg) from exc mo = app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo' assert mo.is_file(), 'msgfmt failed' diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 51e6bd4d3..07f101d7a 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -74,7 +74,8 @@ def tail_check(check): for node in nodes: if node.tail and rex.search(node.tail): return True - raise AssertionError(f'{check!r} not found in tail of any nodes {nodes}') + msg = f'{check!r} not found in tail of any nodes {nodes}' + raise AssertionError(msg) return checker diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 38341c55d..e37a97edb 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -72,8 +72,8 @@ def compile_latex_document(app, filename='python.tex', docclass='manual'): except CalledProcessError as exc: print(exc.stdout.decode('utf8')) print(exc.stderr.decode('utf8')) - raise AssertionError(f'{app.config.latex_engine} exited with ' - f'return code {exc.returncode}') from exc + msg = f'{app.config.latex_engine} exited with return code {exc.returncode}' + raise AssertionError(msg) from exc def skip_if_requested(testfunc): diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 97cc80c2b..99643826d 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -56,7 +56,8 @@ def test_texinfo(app, status, warning): except CalledProcessError as exc: print(exc.stdout) print(exc.stderr) - raise AssertionError(f'makeinfo exited with return code {exc.retcode}') from exc + msg = f'makeinfo exited with return code {exc.retcode}' + raise AssertionError(msg) from exc @pytest.mark.sphinx('texinfo', testroot='markup-rubric') diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index d4cc3900f..6582a0c0c 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -70,7 +70,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("Input: ", input) print("Result: ", res) print("Expected: ", outputAst) - raise DefinitionError("") + raise DefinitionError rootSymbol = Symbol(None, None, None, None, None) symbol = rootSymbol.add_declaration(ast, docname="TestDoc", line=42) parentNode = addnodes.desc() @@ -83,7 +83,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("Input: ", input) print("astext(): ", resAsText) print("Expected: ", outputAsText) - raise DefinitionError("") + raise DefinitionError idExpected = [None] for i in range(1, _max_id + 1): @@ -113,7 +113,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("result: %s" % idActual[i]) print("expected: %s" % idExpected[i]) # print(rootSymbol.dump(0)) - raise DefinitionError("") + raise DefinitionError def check(name, input, idDict, output=None, key=None, asTextOutput=None): @@ -142,7 +142,7 @@ def test_domain_c_ast_expressions(): print("Input: ", input) print("Result: ", res) print("Expected: ", output) - raise DefinitionError("") + raise DefinitionError displayString = ast.get_display_string() if res != displayString: # note: if the expression contains an anon name then this will trigger a falsely @@ -150,7 +150,7 @@ def test_domain_c_ast_expressions(): print("Input: ", expr) print("Result: ", res) print("Display: ", displayString) - raise DefinitionError("") + raise DefinitionError # type expressions exprCheck('int*') @@ -617,7 +617,7 @@ def test_extra_keywords(): # # used for getting all the ids out for checking # for a in ids: # print(a) -# raise DefinitionError("") +# raise DefinitionError def split_warnigns(warning): diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 45465c463..dcc2b0f39 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -71,7 +71,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("Input: ", input) print("Result: ", res) print("Expected: ", outputAst) - raise DefinitionError("") + raise DefinitionError rootSymbol = Symbol(None, None, None, None, None, None, None) symbol = rootSymbol.add_declaration(ast, docname="TestDoc", line=42) parentNode = addnodes.desc() @@ -85,7 +85,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("astext(): ", resAsText) print("Expected: ", outputAsText) print("Node:", parentNode) - raise DefinitionError("") + raise DefinitionError idExpected = [None] for i in range(1, _max_id + 1): @@ -115,7 +115,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("result: %s" % idActual[i]) print("expected: %s" % idExpected[i]) print(rootSymbol.dump(0)) - raise DefinitionError("") + raise DefinitionError def check(name, input, idDict, output=None, key=None, asTextOutput=None): @@ -187,7 +187,7 @@ def test_domain_cpp_ast_expressions(): print("") print("Input: ", expr) print("Result: ", res) - raise DefinitionError("") + raise DefinitionError displayString = ast.get_display_string() if res != displayString: # note: if the expression contains an anon name then this will trigger a falsely @@ -195,7 +195,7 @@ def test_domain_cpp_ast_expressions(): print("Input: ", expr) print("Result: ", res) print("Display: ", displayString) - raise DefinitionError("") + raise DefinitionError # primary exprCheck('nullptr', 'LDnE') @@ -1101,7 +1101,7 @@ def test_domain_cpp_template_parameters_is_pack(param: str, is_pack: bool): # # used for getting all the ids out for checking # for a in ids: # print(a) -# raise DefinitionError("") +# raise DefinitionError def filter_warnings(warning, file): diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index cd49b5244..9e2367fe3 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -29,9 +29,11 @@ def has_binary(binary): def test_imgmath_png(app, status, warning): app.builder.build_all() if "LaTeX command 'latex' cannot be run" in warning.getvalue(): - raise pytest.skip.Exception('LaTeX command "latex" is not available') + msg = 'LaTeX command "latex" is not available' + raise pytest.skip.Exception(msg) if "dvipng command 'dvipng' cannot be run" in warning.getvalue(): - raise pytest.skip.Exception('dvipng command "dvipng" is not available') + msg = 'dvipng command "dvipng" is not available' + raise pytest.skip.Exception(msg) content = (app.outdir / 'index.html').read_text(encoding='utf8') shutil.rmtree(app.outdir) @@ -48,9 +50,11 @@ def test_imgmath_png(app, status, warning): def test_imgmath_svg(app, status, warning): app.builder.build_all() if "LaTeX command 'latex' cannot be run" in warning.getvalue(): - raise pytest.skip.Exception('LaTeX command "latex" is not available') + msg = 'LaTeX command "latex" is not available' + raise pytest.skip.Exception(msg) if "dvisvgm command 'dvisvgm' cannot be run" in warning.getvalue(): - raise pytest.skip.Exception('dvisvgm command "dvisvgm" is not available') + msg = 'dvisvgm command "dvisvgm" is not available' + raise pytest.skip.Exception(msg) content = (app.outdir / 'index.html').read_text(encoding='utf8') shutil.rmtree(app.outdir) @@ -68,9 +72,11 @@ def test_imgmath_svg(app, status, warning): def test_imgmath_svg_embed(app, status, warning): app.builder.build_all() if "LaTeX command 'latex' cannot be run" in warning.getvalue(): - pytest.skip('LaTeX command "latex" is not available') + msg = 'LaTeX command "latex" is not available' + raise pytest.skip.Exception(msg) if "dvisvgm command 'dvisvgm' cannot be run" in warning.getvalue(): - pytest.skip('dvisvgm command "dvisvgm" is not available') + msg = 'dvisvgm command "dvisvgm" is not available' + raise pytest.skip.Exception(msg) content = (app.outdir / 'index.html').read_text(encoding='utf8') shutil.rmtree(app.outdir) diff --git a/tests/test_util_display.py b/tests/test_util_display.py index b4fc89203..9ecdd6a54 100644 --- a/tests/test_util_display.py +++ b/tests/test_util_display.py @@ -78,7 +78,7 @@ def test_progress_message(app, status, warning): # skipping case with progress_message('testing'): - raise SkipProgressMessage('Reason: %s', 'error') + raise SkipProgressMessage('Reason: %s', 'error') # NoQA: EM101 output = strip_escseq(status.getvalue()) assert 'testing... skipped\nReason: error\n' in output diff --git a/utils/bump_version.py b/utils/bump_version.py index d8a26960c..6e507551a 100755 --- a/utils/bump_version.py +++ b/utils/bump_version.py @@ -171,7 +171,8 @@ def main(): if changes.in_development: changes.finalize_release_date() else: - raise Skip('version not changed') + reason = 'version not changed' + raise Skip(reason) else: if changes.in_development: print('WARNING: last version is not released yet: %s' % changes.version)