From ea0f2c82d7d258ab19ce55e2254666bf019f5d45 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 18:39:49 +0800 Subject: [PATCH 01/11] Docs: Show typing info and parse INPUT/OUTPUT sections --- pyproject.toml | 1 + src/sage/manifolds/differentiable/diff_map.py | 2 - src/sage_docbuild/conf.py | 9 + src/sage_docbuild/ext/sage_syntax.py | 57 ++ src/sage_docbuild/ext/sage_transformer.py | 556 ++++++++++++++++++ .../ext/sage_transformer_test.py | 171 ++++++ 6 files changed, 794 insertions(+), 2 deletions(-) create mode 100644 src/sage_docbuild/ext/sage_syntax.py create mode 100644 src/sage_docbuild/ext/sage_transformer.py create mode 100644 src/sage_docbuild/ext/sage_transformer_test.py diff --git a/pyproject.toml b/pyproject.toml index d4d023ae190..6def7d0752e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -212,6 +212,7 @@ docs = [ "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", + "sphinx-autodoc-typehints", "jupyter-sphinx", "furo", "python-dateutil", diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index 6ac44fa7fb6..4bcaa90aadc 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -854,8 +854,6 @@ def pullback(self, tensor_or_codomain_subset, name=None, latex_name=None): INPUT: - One of the following: - - ``tensor_or_codomain_subset`` -- one of the following: - a :class:`~sage.manifolds.differentiable.tensorfield.TensorField`; diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index da9da0361ca..f27b3d48d8e 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -47,16 +47,25 @@ 'sage_docbuild.ext.inventory_builder', 'sage_docbuild.ext.multidocs', 'sage_docbuild.ext.sage_autodoc', + 'sage_docbuild.ext.sage_syntax', 'sphinx.ext.todo', 'sphinx.ext.extlinks', 'sphinx.ext.mathjax', 'sphinx.ext.linkcode', 'sphinx_copybutton', 'sphinx_inline_tabs', + 'sphinx_autodoc_typehints', 'IPython.sphinxext.ipython_directive', 'matplotlib.sphinxext.plot_directive', ] +# Add stub documentation for undocumented parameters to be able to add type info +always_document_param_types = True +# Display Union's using the | operator +always_use_bars_union = True +# Adds ", default: ..." after the type hints for parameters +# typehints_defaults = 'comma' + if JupyterSphinx().is_present(): extensions.append('jupyter_sphinx') diff --git a/src/sage_docbuild/ext/sage_syntax.py b/src/sage_docbuild/ext/sage_syntax.py new file mode 100644 index 00000000000..d62da5d9a35 --- /dev/null +++ b/src/sage_docbuild/ext/sage_syntax.py @@ -0,0 +1,57 @@ +""" +Sphinx extension to translate Sage-specific markers to Sphinx field list entries. + +Example:: + + INPUT: + - ``FOO`` -- Short description. + + OUTPUT: + Result description. + + → becomes:: + + :param FOO: Short description. + :returns: Result description. +""" + +from __future__ import annotations + +from typing import List + +from sphinx.application import Sphinx + +from sage_docbuild.ext.sage_transformer import DoctestTransformer + + +def _process_docstring( + app: Sphinx, what: str, name: str, obj: object, options: object, lines: List[str] +) -> None: + """ + Process a docstring after autodoc has collected it. + + Called when autodoc has read and processed a docstring. + + INPUT: + - app -- Sphinx application instance. + - what -- Object type (module, class, exception, function, method, attribute). + - name -- Fully qualified object name. + - obj -- The object whose docstring is being processed. + - options -- Autodoc directive options. + - lines -- Mutable list of docstring lines (modified in place). + """ + if not lines: + return + + transformer = DoctestTransformer(lines, what, name, obj) + lines[:] = transformer.transform() + + +def setup(app: Sphinx): + app.setup_extension("sage_docbuild.ext.sage_autodoc") + app.connect("autodoc-process-docstring", _process_docstring) + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/src/sage_docbuild/ext/sage_transformer.py b/src/sage_docbuild/ext/sage_transformer.py new file mode 100644 index 00000000000..27bcbdad8a7 --- /dev/null +++ b/src/sage_docbuild/ext/sage_transformer.py @@ -0,0 +1,556 @@ +"""Classes for docstring parsing and formatting.""" +# https://github.com/sphinx-doc/sphinx/tree/master/sphinx/ext/napoleon + +from __future__ import annotations + +import collections +import contextlib +import inspect +import re +from typing import TYPE_CHECKING, Any + +from sphinx.util import logging +from sphinx.util.typing import get_type_hints, stringify_annotation + +if TYPE_CHECKING: + from collections.abc import Callable + + +logger = logging.getLogger(__name__) + +# "
: " +_section_regex = re.compile(r"^\s*([A-Z]+):+\s*$") +# - -- +_field_regex = r"^\s*-\s*(?P.+)\s*--\s*(?P.*)$" + +_single_colon_regex = re.compile(r"(?`))" +) +_bullet_list_regex = re.compile(r"^(\*|\+|\-)(\s+\S|\s*$)") +_enumerated_list_regex = re.compile( + r"^(?P\()?" + r"(\d+|#|[ivxlcdm]+|[IVXLCDM]+|[a-zA-Z])" + r"(?(paren)\)|\.)(\s+\S|\s*$)" +) + + +class Deque[T](collections.deque[T]): + """A subclass of deque that mimics ``pockets.iterators.modify_iter``. + + The `.Deque.get` and `.Deque.next` methods are added. + """ + + sentinel = object() + + def get(self, n: int) -> T: + """Return the nth element of the stack, or ``self.sentinel`` if n is + greater than the stack size. + """ + return self[n] if n < len(self) else self.sentinel + + def next(self) -> T: + if self: + return super().popleft() + else: + raise StopIteration + + +class DoctestTransformer: + """Convert Sage docstrings to reStructuredText.""" + + _name_rgx = re.compile( + r"^\s*((?::(?P\S+):)?`(?P~?[a-zA-Z0-9_.-]+)`|" + r" (?P~?[a-zA-Z0-9_.-]+))\s*", + re.VERBOSE, + ) + + def __init__( + self, + lines: list[str], + what: str = "", + name: str = "", + obj: Any = None, + ) -> None: + if not what: + if inspect.isclass(obj): + what = "class" + elif inspect.ismodule(obj): + what = "module" + elif callable(obj): + what = "function" + else: + what = "object" + + self._what = what + self._name = name + self._obj = obj + self._lines: Deque[str] = Deque(map(str.rstrip, lines)) + self._is_in_section = False + self._section_indent = 0 + self._sections: dict[str, Callable[..., list[str]]] = { + "input": self._parse_parameters_section, + "output": self._parse_returns_section, + #'examples': self._parse_examples_section, + #'algorithm': partial(self._parse_admonition, 'algorithm'), + #'references': partial(self._parse_admonition, 'references'), + #'authors': partial(self._parse_admonition, 'authors'), + } + + def _get_location(self) -> str | None: + try: + filepath = inspect.getfile(self._obj) if self._obj is not None else None + except TypeError: + filepath = None + name = self._name + + if filepath is None and name is None: + return None + elif filepath is None: + filepath = "" + + return f"{filepath}:docstring of {name}" + + def _consume_indented_block(self, indent: int = 1) -> list[str]: + lines = [] + line = self._lines.get(0) + while not self._is_section_break() and ( + not line or self._is_indented(line, indent) + ): + lines.append(self._lines.next()) + line = self._lines.get(0) + return lines + + def _consume_contiguous(self) -> list[str]: + lines = [] + while self._lines and self._lines.get(0) and not self._is_section_header(): + lines.append(self._lines.next()) + return lines + + def _consume_empty(self) -> list[str]: + lines = [] + line = self._lines.get(0) + while self._lines and not line: + lines.append(self._lines.next()) + line = self._lines.get(0) + return lines + + def _consume_field(self) -> tuple[str, list[str]]: + """ + Consume a single field/parameter from the docstring. + """ + line = self._lines.next() + match = re.match(_field_regex, line) + if match: + _name = self._escape_args_and_kwargs(match.group("name").strip()) + _desc = match.group("rest").strip() + indent = self._get_indent(line) + 1 + _descs = [_desc, *self._dedent(self._consume_indented_block(indent))] + _descs = self.__class__(_descs).transform() + return _name, _descs + else: + raise ValueError(f"Invalid field line: {line}") + + def _consume_fields(self, multiple: bool = False) -> list[tuple[str, list[str]]]: + """ + Consume all fields/parameters from the docstring, + until a section break is encountered. + """ + self._consume_empty() + fields: list[tuple[str, list[str]]] = [] + while not self._is_section_break(): + _name, _desc = self._consume_field() + if multiple and _name: + fields.extend( + (name.strip().strip(r"`").strip(), _desc) for name in _name.split(",") + ) + elif _name or _desc: + fields.append((_name.strip().strip(r"`").strip(), _desc)) + return fields + + def _consume_inline_attribute(self) -> tuple[str, list[str]]: + line = self._lines.next() + _type, colon, _desc = self._partition_field_on_colon(line) + if not colon or not _desc: + _type, _desc = _desc, _type + _desc += colon + _descs = [_desc, *self._dedent(self._consume_to_end())] + _descs = self.__class__(_descs).transform() + return _type, _descs + + def _consume_returns_section(self) -> list[tuple[str, list[str]]]: + lines = self._dedent(self._consume_to_next_section()) + if not lines: + return [] + transformed = self.__class__(lines).transform() + return [("", transformed)] + + def _consume_section_header(self) -> str: + section = self._lines.next() + return section.strip().strip(":").lower() + + def _consume_to_end(self) -> list[str]: + lines = [] + while self._lines: + lines.append(self._lines.next()) + return lines + + def _consume_to_next_section(self) -> list[str]: + self._consume_empty() + lines = [] + while not self._is_section_break(): + lines.append(self._lines.next()) + return lines + self._consume_empty() + + def _dedent(self, lines: list[str], full: bool = False) -> list[str]: + if full: + return [line.lstrip() for line in lines] + else: + min_indent = self._get_min_indent(lines) + return [line[min_indent:] for line in lines] + + def _escape_args_and_kwargs(self, name: str) -> str: + if name[:2] == "**": + return r"\*\*" + name[2:] + elif name[:1] == "*": + return r"\*" + name[1:] + else: + return name + + def _fix_field_desc(self, desc: list[str]) -> list[str]: + if self._is_list(desc): + desc = ["", *desc] + elif desc[0].endswith("::"): + desc_block = desc[1:] + indent = self._get_indent(desc[0]) + block_indent = self._get_initial_indent(desc_block) + if block_indent > indent: + desc = ["", *desc] + else: + desc = ["", desc[0], *self._indent(desc_block, 4)] + return desc + + def _format_admonition(self, admonition: str, lines: list[str]) -> list[str]: + lines = self._strip_empty(lines) + if len(lines) == 1: + return [f".. {admonition}:: {lines[0].strip()}", ""] + elif lines: + lines = self._indent(self._dedent(lines), 3) + return [f".. {admonition}::", "", *lines, ""] + else: + return [f".. {admonition}::", ""] + + def _format_block( + self, + prefix: str, + lines: list[str], + padding: str | None = None, + ) -> list[str]: + if lines: + if padding is None: + padding = " " * len(prefix) + result_lines = [] + for i, line in enumerate(lines): + if i == 0: + result_lines.append((prefix + line).rstrip()) + elif line: + result_lines.append(padding + line) + else: + result_lines.append("") + return result_lines + else: + return [prefix] + + def _format_docutils_params( + self, + fields: list[tuple[str, list[str]]], + field_role: str = "param", + ) -> list[str]: + lines = [] + for _name, _desc in fields: + _desc = self._strip_empty(_desc) + if any(_desc): + _desc = self._fix_field_desc(_desc) + field = f":{field_role} {_name}: " + lines.extend(self._format_block(field, _desc)) + else: + lines.append(f":{field_role} {_name}:") + return [*lines, ""] + + def _format_field(self, _name: str, _desc: list[str]) -> list[str]: + _desc = self._strip_empty(_desc) + has_desc = any(_desc) + separator = " -- " if has_desc else "" + if _name: + field = f"**{_name}**{separator}" + else: + field = "" + + if has_desc: + _desc = self._fix_field_desc(_desc) + if _desc[0]: + return [field + _desc[0], *_desc[1:]] + else: + return [field, *_desc] + else: + return [field] + + def _format_fields( + self, + field_type: str, + fields: list[tuple[str, list[str]]], + ) -> list[str]: + field_type = f":{field_type.strip()}:" + padding = " " * len(field_type) + multi = len(fields) > 1 + lines: list[str] = [] + for _name, _desc in fields: + field = self._format_field(_name, _desc) + if multi: + if lines: + lines.extend(self._format_block(padding + " * ", field)) + else: + lines.extend(self._format_block(field_type + " * ", field)) + else: + lines.extend(self._format_block(field_type + " ", field)) + if lines and lines[-1]: + lines.append("") + return lines + + def _get_current_indent(self, peek_ahead: int = 0) -> int: + line = self._lines.get(peek_ahead) + while line is not self._lines.sentinel: + if line: + return self._get_indent(line) + peek_ahead += 1 + line = self._lines.get(peek_ahead) + return 0 + + def _get_indent(self, line: str) -> int: + for i, s in enumerate(line): + if not s.isspace(): + return i + return len(line) + + def _get_initial_indent(self, lines: list[str]) -> int: + for line in lines: + if line: + return self._get_indent(line) + return 0 + + def _get_min_indent(self, lines: list[str]) -> int: + min_indent = None + for line in lines: + if line: + indent = self._get_indent(line) + if min_indent is None or indent < min_indent: + min_indent = indent + return min_indent or 0 + + def _indent(self, lines: list[str], n: int = 4) -> list[str]: + return [(" " * n) + line for line in lines] + + def _is_indented(self, line: str, indent: int = 1) -> bool: + for i, s in enumerate(line): + if i >= indent: + return True + elif not s.isspace(): + return False + return False + + def _is_list(self, lines: list[str]) -> bool: + if not lines: + return False + if _bullet_list_regex.match(lines[0]): + return True + if _enumerated_list_regex.match(lines[0]): + return True + if len(lines) < 2 or lines[0].endswith("::"): + return False + indent = self._get_indent(lines[0]) + next_indent = indent + for line in lines[1:]: + if line: + next_indent = self._get_indent(line) + break + return next_indent > indent + + def _is_section_header(self) -> bool: + line = self._lines.get(0) + match = _section_regex.match(line) + if match: + section = match.group(1).lower() + return section in self._sections + return False + + def _is_section_break(self) -> bool: + line = self._lines.get(0) + return ( + not self._lines + or self._is_section_header() + or ( + self._is_in_section + and line is not self._lines.sentinel + and not self._is_indented(line, self._section_indent) + ) + ) + + def transform(self) -> list[str]: + """ + Return the parsed lines of the docstring in reStructuredText format. + """ + _parsed_lines = self._consume_empty() + + if self._name and self._what in {"attribute", "data", "property"}: + res: list[str] = [] + with contextlib.suppress(StopIteration): + res = self._parse_attribute_docstring() + + _parsed_lines.extend(res) + return _parsed_lines + + while self._lines: + if self._is_section_header(): + try: + section = self._consume_section_header() + self._is_in_section = True + self._section_indent = self._get_current_indent() + lines = self._sections[section](section) + finally: + self._is_in_section = False + self._section_indent = 0 + elif not _parsed_lines: + lines = self._consume_contiguous() + self._consume_empty() + else: + lines = self._consume_to_next_section() + _parsed_lines.extend(lines) + return _parsed_lines + + def _parse_admonition(self, admonition: str, section: str) -> list[str]: + lines = self._consume_to_next_section() + return self._format_admonition(admonition, lines) + + def _parse_attribute_docstring(self) -> list[str]: + _type, _desc = self._consume_inline_attribute() + lines = self._format_field("", _desc) + if _type: + lines.extend(["", f":type: {_type}"]) + return lines + + def _parse_examples_section(self, section: str) -> list[str]: + return self._parse_generic_section("Examples", False) + + def _parse_custom_generic_section(self, section: str) -> list[str]: + # for now, no admonition for simple custom sections + return self._parse_generic_section(section, False) + + def _parse_custom_params_style_section(self, section: str) -> list[str]: + return self._format_fields(section, self._consume_fields()) + + def _parse_custom_returns_style_section(self, section: str) -> list[str]: + fields = self._consume_returns_section() + return self._format_fields(section, fields) + + def _parse_generic_section(self, section: str, use_admonition: bool) -> list[str]: + lines = self._strip_empty(self._consume_to_next_section()) + lines = self._dedent(lines) + if use_admonition: + header = f".. admonition:: {section}" + lines = self._indent(lines, 3) + else: + header = f".. rubric:: {section}" + if lines: + return [header, "", *lines, ""] + else: + return [header, ""] + + def _parse_parameters_section(self, section: str) -> list[str]: + fields = self._consume_fields(multiple=True) + return self._format_docutils_params(fields) + + def _parse_returns_section(self, section: str) -> list[str]: + fields = self._consume_returns_section() + multi = len(fields) > 1 + lines: list[str] = [] + + for _name, _desc in fields: + field = self._format_field(_name, _desc) + if multi: + if lines: + lines.extend(self._format_block(" * ", field)) + else: + lines.extend(self._format_block(":returns: * ", field)) + elif any(field): # only add :returns: if there's something to say + lines.extend(self._format_block(":returns: ", field)) + if lines and lines[-1]: + lines.append("") + return lines + + def _partition_field_on_colon(self, line: str) -> tuple[str, str, str]: + before_colon = [] + after_colon = [] + colon = "" + found_colon = False + for i, source in enumerate(_xref_or_code_regex.split(line)): + if found_colon: + after_colon.append(source) + else: + m = _single_colon_regex.search(source) + if (i % 2) == 0 and m: + found_colon = True + colon = source[m.start() : m.end()] + before_colon.append(source[: m.start()]) + after_colon.append(source[m.end() :]) + else: + before_colon.append(source) + + return "".join(before_colon).strip(), colon, "".join(after_colon).strip() + + def _strip_empty(self, lines: list[str]) -> list[str]: + if lines: + start = -1 + for i, line in enumerate(lines): + if line: + start = i + break + if start == -1: + lines = [] + end = -1 + for i in reversed(range(len(lines))): + line = lines[i] + if line: + end = i + break + if start > 0 or end + 1 < len(lines): + lines = lines[start : end + 1] + return lines + + def _lookup_annotation(self, _name: str) -> str: + if False: # True is default + if self._what in {"module", "class", "exception"} and self._obj: + # cache the class annotations + if not hasattr(self, "_annotations"): + localns = getattr(self._config, "autodoc_type_aliases", {}) + localns.update( + getattr( + self._config, + "napoleon_type_aliases", + {}, + ) + or {} + ) + self._annotations = get_type_hints(self._obj, None, localns) + if _name in self._annotations: + short_literals = getattr( + self._config, "python_display_short_literal_types", False + ) + return stringify_annotation( + self._annotations[_name], + mode="fully-qualified-except-typing", + short_literals=short_literals, + ) + # No annotation found + return "" diff --git a/src/sage_docbuild/ext/sage_transformer_test.py b/src/sage_docbuild/ext/sage_transformer_test.py new file mode 100644 index 00000000000..ec857366aae --- /dev/null +++ b/src/sage_docbuild/ext/sage_transformer_test.py @@ -0,0 +1,171 @@ +import pytest + +from .sage_transformer import DoctestTransformer + + +def test_consume_field_simple(): + lines = ["- param -- description"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == "param" + assert descs == ["description"] + + +def test_consume_field_multiple_params(): + lines = ["- param1, param2 -- description"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == "param1, param2" + assert descs == ["description"] + + +def test_consume_field_with_blank_lines_in_description(): + lines = [ + "- param -- first line", + " ", + " second line", + "- another -- x", + ] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == "param" + assert descs == ["first line", "", "second line"] + + +def test_consume_field_escaping_stars(): + lines = ["- *args -- star args"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == r"\*args" + assert descs == ["star args"] + + lines = ["- **kwargs -- kw args"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == r"\*\*kwargs" + assert descs == ["kw args"] + + +def test_consume_field_invalid_line_raises(): + lines = ["param -- missing leading dash"] + dt = DoctestTransformer(lines) + with pytest.raises(ValueError): + dt._consume_field() + + +def test_consume_fields_simple(): + lines = [ + "- param1 -- first param", + "- param2 -- second param", + ] + dt = DoctestTransformer(lines) + fields = dt._consume_fields() + assert fields == [ + ("param1", ["first param"]), + ("param2", ["second param"]), + ] + + +def test_consume_fields_multiple_flag_splits_names(): + lines = ["- x, y -- coordinate values"] + dt = DoctestTransformer(lines) + fields = dt._consume_fields(multiple=True) + assert fields == [ + ("x", ["coordinate values"]), + ("y", ["coordinate values"]), + ] + # Both entries share the same description list object + assert fields[0][1] is fields[1][1] + + +def test_consume_fields_multiple_flag_false_no_split(): + lines = ["- x, y -- coordinate values"] + dt = DoctestTransformer(lines) + fields = dt._consume_fields(multiple=False) + assert fields == [ + ("x, y", ["coordinate values"]), + ] + + +def test_consume_fields_trims_backticks_and_splits(): + lines = ["- ``x``, ``y`` -- desc"] + dt = DoctestTransformer(lines) + fields = dt._consume_fields(multiple=True) + assert fields == [ + ("x", ["desc"]), + ("y", ["desc"]), + ] + + +def test_consume_fields_skips_leading_blank_lines(): + lines = [ + "", + " ", + "- param -- value", + ] + dt = DoctestTransformer(lines) + fields = dt._consume_fields() + assert fields == [("param", ["value"])] + + +def test_consume_fields_stops_at_section_header(): + lines = [ + "- p1 -- first", + "INPUT:", + "- p2 -- second", # should remain untouched + ] + dt = DoctestTransformer(lines) + fields = dt._consume_fields() + assert fields == [("p1", ["first"])] + # Header not consumed + assert dt._lines.get(0) == "INPUT:" + + +def test_consume_returns_section_basic(): + lines = [ + "result line 1", + "", + "OUTPUT:", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["result line 1", ""])] + # Header not consumed + assert dt._lines.get(0) == "OUTPUT:" + + +def test_consume_returns_section_indented_dedents(): + lines = [ + " first line", + " second deeper", + "OUTPUT:", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["first line", " second deeper"])] + assert dt._lines.get(0) == "OUTPUT:" + + +def test_consume_returns_section_empty(): + lines = [ + "", + "", + "OUTPUT:", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [] + assert dt._lines.get(0) == "OUTPUT:" + + +def test_consume_returns_section_trailing_blank_lines_preserved(): + lines = [ + "desc", + "", + "", + "OUTPUT:", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["desc", "", ""])] + assert dt._lines.get(0) == "OUTPUT:" From 5ae3584e7272c88ffbd9e8031165c3ad36dbc887 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 18:45:15 +0800 Subject: [PATCH 02/11] Update conda lock files --- environment-3.11-linux.yml | 170 +++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 84 deletions(-) diff --git a/environment-3.11-linux.yml b/environment-3.11-linux.yml index ea0d4a65452..dd2ca5d8fbe 100644 --- a/environment-3.11-linux.yml +++ b/environment-3.11-linux.yml @@ -1,7 +1,7 @@ name: sage-dev # Generated by conda-lock. # platform: linux-64 -# input_hash: f2c73a276d552c1cee592849e39d4da886802f0828819a0282888079208c1f48 +# input_hash: a8a25255fc5cd6d8e3d469477dbce9118817fbaa8624d4c043c42427c7d864f7 channels: - conda-forge @@ -27,8 +27,8 @@ dependencies: - binutils=2.44=h4852527_1 - binutils_impl_linux-64=2.44=h4bf12b8_1 - binutils_linux-64=2.44=h4852527_1 - - blas=2.132=openblas - - blas-devel=3.9.0=32_h1ea3ea9_openblas + - blas=2.134=openblas + - blas-devel=3.9.0=34_h1ea3ea9_openblas - bleach=6.2.0=pyh29332c3_4 - bleach-with-css=6.2.0=h82add2a_4 - bliss=0.77=h00ab1b0_1 @@ -41,37 +41,38 @@ dependencies: - bwidget=1.10.1=ha770c72_1 - bzip2=1.0.8=h4bc722e_7 - c-ares=1.34.5=hb9d3cd8_0 - - c-compiler=1.10.0=h2b85faf_0 - - ca-certificates=2025.7.14=hbd8a1cb_0 + - c-compiler=1.11.0=h4d9bdce_0 + - ca-certificates=2025.8.3=hbd8a1cb_0 - cachecontrol=0.14.3=pyha770c72_0 - cachecontrol-with-filecache=0.14.3=pyhd8ed1ab_0 - cairo=1.18.4=h3394656_0 - cddlib=1!0.94m=h9202a9a_0 - - certifi=2025.7.14=pyhd8ed1ab_0 + - certifi=2025.8.3=pyhd8ed1ab_0 - cffi=1.17.1=py311hf29c0ef_0 - - charset-normalizer=3.4.2=pyhd8ed1ab_0 + - charset-normalizer=3.4.3=pyhd8ed1ab_0 - click=8.2.1=pyh707e725_0 - click-default-group=1.2.4=pyhd8ed1ab_1 - cliquer=1.22=hd590300_1 - colorama=0.4.6=pyhd8ed1ab_1 - - comm=0.2.2=pyhd8ed1ab_1 + - comm=0.2.3=pyhe01879c_0 + - conda-gcc-specs=14.3.0=hb991d5c_4 - conda-lock=3.0.4=pyh367d9c9_1 - conda-souschef=2.2.3=pyhd8ed1ab_0 - - contourpy=1.3.2=py311hd18a35c_0 + - contourpy=1.3.3=py311hdf67eae_1 - conway-polynomials=0.10=pyhd8ed1ab_1 - - coverage=7.9.2=py311h2dc5d0c_0 + - coverage=7.10.4=py311h3778330_0 - cpython=3.11.13=py311hd8ed1ab_0 - crashtest=0.4.1=pyhd8ed1ab_1 - - cryptography=45.0.5=py311hafd3f86_0 + - cryptography=45.0.6=py311h8488d03_0 - curl=8.14.1=h332b0f4_0 - - cxx-compiler=1.10.0=h1a2810e_0 + - cxx-compiler=1.11.0=hfcd1e18_0 - cycler=0.12.1=pyhd8ed1ab_1 - cypari2=2.2.2=py311h8699650_0 - cyrus-sasl=2.1.28=hd9c7081_0 - cysignals=1.12.3=py311hfdbb021_0 - - cython=3.1.2=py311ha3e34f5_2 + - cython=3.1.3=py311h91b4c63_2 - dbus=1.16.2=h3c4dab8_0 - - debugpy=1.8.15=py311hc665b79_0 + - debugpy=1.8.16=py311hc665b79_0 - decorator=5.2.1=pyhd8ed1ab_0 - defusedxml=0.7.1=pyhd8ed1ab_0 - deprecated=1.2.18=pyhd8ed1ab_0 @@ -88,7 +89,7 @@ dependencies: - executing=2.2.0=pyhd8ed1ab_0 - expat=2.7.1=hecca717_0 - fflas-ffpack=2.5.0=h4f9960b_0 - - filelock=3.18.0=pyhd8ed1ab_0 + - filelock=3.19.1=pyhd8ed1ab_0 - flake8=7.3.0=pyhd8ed1ab_0 - flake8-rst-docstrings=0.3.1=pyhd8ed1ab_0 - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 @@ -98,8 +99,8 @@ dependencies: - fontconfig=2.15.0=h7e30c49_1 - fonts-conda-ecosystem=1=0 - fonts-conda-forge=1=0 - - fonttools=4.59.0=py311h3778330_0 - - fortran-compiler=1.10.0=h36df796_0 + - fonttools=4.59.1=py311h3778330_0 + - fortran-compiler=1.11.0=h9bea470_0 - fplll=5.5.0=hd20a173_0 - fpylll=0.6.3=py311hf0b6740_0 - freetype=2.13.3=ha770c72_1 @@ -107,28 +108,28 @@ dependencies: - furo=2024.8.6=pyhd8ed1ab_2 - gap-core=4.14.0=h3b03731_5 - gap-defaults=4.14.0=ha770c72_5 - - gcc=13.3.0=h9576a4e_2 - - gcc_impl_linux-64=13.3.0=h1e990d8_2 - - gcc_linux-64=13.3.0=h6f18a23_11 + - gcc=14.3.0=h76bdaa0_4 + - gcc_impl_linux-64=14.3.0=hd9e9e21_4 + - gcc_linux-64=14.3.0=h1382650_11 - gf2x=1.3.0=h55551d5_3 - gfan=0.6.2=hb86e20a_1003 - - gfortran=13.3.0=h9576a4e_2 - - gfortran_impl_linux-64=13.3.0=h84c1745_2 - - gfortran_linux-64=13.3.0=h1917dac_11 + - gfortran=14.3.0=he448592_4 + - gfortran_impl_linux-64=14.3.0=h7db7018_4 + - gfortran_linux-64=14.3.0=h30a37f7_11 - gitdb=4.0.12=pyhd8ed1ab_0 - - gitpython=3.1.44=pyhff2d567_0 + - gitpython=3.1.45=pyhff2d567_0 - givaro=4.2.0=hb397f18_2 - glpk=5.0=h445213a_0 - gmp=6.3.0=hac33072_2 - gmpy2=2.1.5=py311h0f6cedb_3 - - graphite2=1.3.14=h5888daf_0 + - graphite2=1.3.14=hecca717_2 - grayskull=2.9.1=pyhd8ed1ab_0 - gsl=2.8=hbf7d49c_1 - - gxx=13.3.0=h9576a4e_2 - - gxx_impl_linux-64=13.3.0=hae580e1_2 - - gxx_linux-64=13.3.0=hb14504d_11 + - gxx=14.3.0=he448592_4 + - gxx_impl_linux-64=14.3.0=he663afc_4 + - gxx_linux-64=14.3.0=ha7acb78_11 - h2=4.2.0=pyhd8ed1ab_0 - - harfbuzz=11.3.2=hbb57e21_0 + - harfbuzz=11.4.1=h15599e2_0 - hpack=4.1.0=pyhd8ed1ab_0 - hyperframe=6.1.0=pyhd8ed1ab_0 - icu=75.1=he02047a_0 @@ -139,17 +140,17 @@ dependencies: - importlib-metadata=8.7.0=pyhe01879c_1 - importlib_resources=6.5.2=pyhd8ed1ab_0 - iniconfig=2.0.0=pyhd8ed1ab_1 - - ipykernel=6.29.5=pyh3099207_0 + - ipykernel=6.30.1=pyh82676e8_0 - ipython=9.4.0=pyhfa0c392_0 - ipython_pygments_lexers=1.1.1=pyhd8ed1ab_0 - ipywidgets=8.1.7=pyhd8ed1ab_0 - jaraco.classes=3.4.0=pyhd8ed1ab_2 - jaraco.context=6.0.1=pyhd8ed1ab_0 - - jaraco.functools=4.2.1=pyhd8ed1ab_0 + - jaraco.functools=4.3.0=pyhd8ed1ab_0 - jedi=0.19.2=pyhd8ed1ab_1 - jeepney=0.9.0=pyhd8ed1ab_0 - jinja2=3.1.6=pyhd8ed1ab_0 - - jsonschema=4.25.0=pyhe01879c_0 + - jsonschema=4.25.1=pyhe01879c_0 - jsonschema-specifications=2025.4.1=pyh29332c3_0 - jupyter-sphinx=0.5.3=pyha770c72_5 - jupyter_client=8.6.3=pyhd8ed1ab_1 @@ -158,14 +159,14 @@ dependencies: - jupyterlab_widgets=3.0.15=pyhd8ed1ab_0 - kernel-headers_linux-64=4.18.0=he073ed8_8 - keyring=25.6.0=pyha804496_0 - - keyutils=1.6.1=h166bdaf_0 - - kiwisolver=1.4.8=py311hd18a35c_1 + - keyutils=1.6.3=hb9d3cd8_0 + - kiwisolver=1.4.9=py311h724c32c_0 - krb5=1.21.3=h659f571_0 - lcalc=2.1.0=h9cf73fc_1 - lcms2=2.17=h717163a_0 - ld_impl_linux-64=2.44=h1423503_1 - lerc=4.0.0=h0aef613_1 - - libblas=3.9.0=32_h59b9bed_openblas + - libblas=3.9.0=34_h59b9bed_openblas - libboost=1.85.0=h0ccab89_4 - libboost-devel=1.85.0=h00ab1b0_4 - libboost-headers=1.85.0=ha770c72_4 @@ -174,7 +175,7 @@ dependencies: - libbrotlicommon=1.1.0=hb9d3cd8_3 - libbrotlidec=1.1.0=hb9d3cd8_3 - libbrotlienc=1.1.0=hb9d3cd8_3 - - libcblas=3.9.0=32_he106b2a_openblas + - libcblas=3.9.0=34_he106b2a_openblas - libclang-cpp20.1=20.1.8=default_hddf928d_0 - libclang13=20.1.8=default_ha444ac7_0 - libcups=2.3.3=hb8b1518_5 @@ -189,48 +190,48 @@ dependencies: - libflint=3.2.2=h754cb6e_0 - libfreetype=2.13.3=ha770c72_1 - libfreetype6=2.13.3=h48d6fc4_1 - - libgcc=15.1.0=h767d61c_3 - - libgcc-devel_linux-64=13.3.0=hc03c837_102 - - libgcc-ng=15.1.0=h69a702a_3 + - libgcc=15.1.0=h767d61c_4 + - libgcc-devel_linux-64=14.3.0=h85bb3a7_104 + - libgcc-ng=15.1.0=h69a702a_4 - libgd=2.3.3=h6f5c62b_11 - - libgfortran=15.1.0=h69a702a_3 - - libgfortran-ng=15.1.0=h69a702a_3 - - libgfortran5=15.1.0=hcea5267_3 + - libgfortran=15.1.0=h69a702a_4 + - libgfortran-ng=15.1.0=h69a702a_4 + - libgfortran5=15.1.0=hcea5267_4 - libgl=1.7.0=ha4b6fd6_2 - - libglib=2.84.2=h3618099_0 + - libglib=2.84.3=hf39c6af_0 - libglvnd=1.7.0=ha4b6fd6_2 - libglx=1.7.0=ha4b6fd6_2 - - libgomp=15.1.0=h767d61c_3 + - libgomp=15.1.0=h767d61c_4 - libhomfly=1.02r6=hd590300_1 - - libiconv=1.18=h4ce23a2_1 + - libiconv=1.18=h3b78370_2 - libjpeg-turbo=3.1.0=hb9d3cd8_0 - - liblapack=3.9.0=32_h7ac8fdf_openblas - - liblapacke=3.9.0=32_he2f377e_openblas + - liblapack=3.9.0=34_h7ac8fdf_openblas + - liblapacke=3.9.0=34_he2f377e_openblas - libllvm20=20.1.8=hecd9e04_0 - liblzma=5.8.1=hb9d3cd8_2 - liblzma-devel=5.8.1=hb9d3cd8_2 - libnghttp2=1.64.0=h161d5f1_0 - libnsl=2.0.1=hb9d3cd8_1 - libntlm=1.8=hb9d3cd8_0 - - libopenblas=0.3.30=pthreads_h94d23a6_0 + - libopenblas=0.3.30=pthreads_h94d23a6_2 - libopengl=1.7.0=ha4b6fd6_2 - libpciaccess=0.18=hb9d3cd8_0 - - libpng=1.6.50=h943b412_0 - - libpq=17.5=h27ae623_0 - - libsanitizer=13.3.0=he8ea267_2 + - libpng=1.6.50=h421ea60_1 + - libpq=17.6=h3675c94_0 + - libsanitizer=14.3.0=hd08acf3_4 - libsodium=1.0.20=h4ab18f5_0 - - libsqlite=3.50.3=hee844dc_1 + - libsqlite=3.50.4=h0c1763c_0 - libssh2=1.11.1=hcf80075_0 - - libstdcxx=15.1.0=h8f9b012_3 - - libstdcxx-devel_linux-64=13.3.0=hc03c837_102 - - libstdcxx-ng=15.1.0=h4852527_3 - - libtiff=4.7.0=hf01ce69_5 + - libstdcxx=15.1.0=h8f9b012_4 + - libstdcxx-devel_linux-64=14.3.0=h85bb3a7_104 + - libstdcxx-ng=15.1.0=h4852527_4 + - libtiff=4.7.0=h8261f1e_6 - libuuid=2.38.1=h0b41bf4_0 - libwebp-base=1.6.0=hd42ef1d_0 - libxcb=1.17.0=h8a09558_0 - libxcrypt=4.4.36=hd590300_1 - - libxkbcommon=1.10.0=h65c71a3_0 - - libxml2=2.13.8=h4bc477f_0 + - libxkbcommon=1.11.0=he8b52b9_0 + - libxml2=2.13.8=h04c0eec_1 - libxslt=1.1.43=h7a3aeb2_0 - libzlib=1.3.1=hb9d3cd8_2 - linbox=1.7.0=h0451620_2 @@ -241,13 +242,13 @@ dependencies: - m4rie=20200125=h051dbe0_0 - make=4.4.1=hb9d3cd8_2 - markupsafe=3.0.2=py311h2dc5d0c_1 - - matplotlib=3.10.3=py311h38be061_0 - - matplotlib-base=3.10.3=py311h2b939e6_0 + - matplotlib=3.10.5=py311h38be061_0 + - matplotlib-base=3.10.5=py311h0f3be63_0 - matplotlib-inline=0.1.7=pyhd8ed1ab_1 - maxima=5.47.0=h75482ee_3 - mccabe=0.7.0=pyhd8ed1ab_1 - memory-allocator=0.1.3=py311h9ecbd09_1 - - meson=1.8.2=pyhe01879c_0 + - meson=1.8.3=pyhe01879c_0 - meson-python=0.18.0=pyh70fd9c4_0 - mistune=3.1.3=pyh29332c3_0 - more-itertools=10.7.0=pyhd8ed1ab_0 @@ -268,11 +269,11 @@ dependencies: - networkx=3.5=pyhe01879c_0 - ninja=1.13.1=h171cf75_0 - ntl=11.4.3=hef3c4d3_1 - - numpy=2.3.1=py311h2e04523_1 - - openblas=0.3.30=pthreads_h6ec200e_0 - - openjpeg=2.5.3=h5fbd93e_0 + - numpy=2.3.2=py311h2e04523_0 + - openblas=0.3.30=pthreads_h6ec200e_2 + - openjpeg=2.5.3=h55fea9a_1 - openldap=2.6.10=he970967_0 - - openssl=3.5.1=h7b32b05_0 + - openssl=3.5.2=h26f9b46_0 - packaging=25.0=pyh29332c3_1 - palp=2.20=h36c2ea0_0 - pandoc=3.7.0.2=ha770c72_0 @@ -289,8 +290,8 @@ dependencies: - pexpect=4.9.0=pyhd8ed1ab_1 - pickleshare=0.7.5=pyhd8ed1ab_1004 - pillow=11.3.0=py311h1322bbf_0 - - pip=25.1.1=pyh8b19718_0 - - pixman=0.46.4=h537e5f6_0 + - pip=25.2=pyh8b19718_0 + - pixman=0.46.4=h54a6638_1 - pkg-config=0.29.2=h4bc722e_1009 - pkgconfig=1.5.5=pyhd8ed1ab_5 - pkginfo=1.12.1.2=pyhd8ed1ab_0 @@ -314,11 +315,11 @@ dependencies: - pydantic=2.11.7=pyh3cfb1c2_0 - pydantic-core=2.33.2=py311hdae7d1d_0 - pyflakes=3.4.0=pyhd8ed1ab_0 - - pygithub=2.6.1=pyhd8ed1ab_0 + - pygithub=2.7.0=pyhd8ed1ab_0 - pygments=2.19.2=pyhd8ed1ab_0 - pyjwt=2.10.1=pyhd8ed1ab_0 - pynacl=1.5.0=py311h9ecbd09_4 - - pyparsing=3.2.3=pyhd8ed1ab_1 + - pyparsing=3.2.3=pyhe01879c_2 - pyproject-metadata=0.9.1=pyhd8ed1ab_0 - pyproject_hooks=1.2.0=pyhd8ed1ab_1 - pyside6=6.9.1=py311h846acb3_0 @@ -326,9 +327,9 @@ dependencies: - pytest=8.4.1=pyhd8ed1ab_0 - pytest-xdist=3.8.0=pyhd8ed1ab_0 - python=3.11.13=h9e4cc4f_0_cpython - - python-build=1.2.2.post1=pyhff2d567_1 + - python-build=1.3.0=pyhff2d567_0 - python-dateutil=2.9.0.post0=pyhe01879c_2 - - python-fastjsonschema=2.21.1=pyhd8ed1ab_0 + - python-fastjsonschema=2.21.2=pyhe01879c_0 - python-installer=0.7.0=pyhff2d567_1 - python-lrcalc=2.1=py311hfdbb021_7 - python-symengine=0.14.0=py311h7b351a7_1 @@ -336,10 +337,10 @@ dependencies: - python_abi=3.11=8_cp311 - pytz=2025.2=pyhd8ed1ab_0 - pyyaml=6.0.2=py311h2dc5d0c_2 - - pyzmq=27.0.0=py311h7deb3e3_0 + - pyzmq=27.0.1=py311hc251a9f_0 - qd=2.3.22=h2cc385e_1004 - qhull=2020.2=h434a139_5 - - qt6-main=6.9.1=h0384650_1 + - qt6-main=6.9.1=h6ac528c_2 - r-base=4.2.3=h2d83a99_19 - r-lattice=0.22_6=r42h57805ef_0 - rapidfuzz=3.13.0=py311hfdbb021_0 @@ -349,17 +350,17 @@ dependencies: - requests-toolbelt=1.0.0=pyhd8ed1ab_1 - restructuredtext_lint=1.4.0=pyhd8ed1ab_1 - roman-numerals-py=3.1.0=pyhd8ed1ab_0 - - rpds-py=0.26.0=py311hdae7d1d_0 + - rpds-py=0.27.0=py311h902ca64_0 - rpy2=3.5.11=py311r42h1f0f07a_3 - ruamel.yaml=0.18.14=py311h9ecbd09_0 - ruamel.yaml.clib=0.2.8=py311h9ecbd09_1 - ruamel.yaml.jinja2=0.2.7=pyhd8ed1ab_1 - - ruff=0.12.4=hf9daec2_0 + - ruff=0.12.9=hbf64f1c_1 - rw=0.9=hd590300_2 - sagemath-db-elliptic-curves=0.8.1=hecc5488_0 - sagemath-db-graphs=20210214=hd8ed1ab_0 - sagemath-db-polytopes=20170220=1 - - scipy=1.16.0=py311h2d3ef60_0 + - scipy=1.16.1=py311h33d6a90_0 - secretstorage=3.3.3=py311h38be061_3 - sed=4.9=h6688a6e_0 - semver=3.0.4=pyhd8ed1ab_0 @@ -373,6 +374,7 @@ dependencies: - snowballstemmer=3.0.1=pyhd8ed1ab_0 - soupsieve=2.7=pyhd8ed1ab_0 - sphinx=8.2.3=pyhd8ed1ab_0 + - sphinx-autodoc-typehints=3.2.0=pyhd8ed1ab_0 - sphinx-basic-ng=1.0.0b2=pyhd8ed1ab_3 - sphinx-copybutton=0.5.2=pyhd8ed1ab_1 - sphinx-inline-tabs=2023.4.21=pyhd8ed1ab_1 @@ -382,7 +384,7 @@ dependencies: - sphinxcontrib-jsmath=1.0.1=pyhd8ed1ab_1 - sphinxcontrib-qthelp=2.0.0=pyhd8ed1ab_1 - sphinxcontrib-serializinghtml=1.1.10=pyhd8ed1ab_1 - - sqlite=3.50.3=heff268d_1 + - sqlite=3.50.4=hbc0de68_0 - stack_data=0.6.3=pyhd8ed1ab_1 - stdlib-list=0.11.1=pyhd8ed1ab_0 - symengine=0.14.0=h064106a_1 @@ -395,13 +397,13 @@ dependencies: - tk=8.6.13=noxft_hd72426e_102 - tktable=2.10=h8d826fa_7 - toml=0.10.2=pyhd8ed1ab_1 - - tomli=2.2.1=pyhd8ed1ab_1 + - tomli=2.2.1=pyhe01879c_2 - tomli-w=1.2.0=pyhd8ed1ab_0 - tomlkit=0.13.3=pyha770c72_0 - - tornado=6.5.1=py311h9ecbd09_0 + - tornado=6.5.2=py311h49ec1c0_0 - tqdm=4.67.1=pyhd8ed1ab_1 - traitlets=5.14.3=pyhd8ed1ab_1 - - trove-classifiers=2025.5.9.12=pyhd8ed1ab_0 + - trove-classifiers=2025.8.6.13=pyhd8ed1ab_0 - typing-extensions=4.14.1=h4440ef1_0 - typing-inspection=0.4.1=pyhd8ed1ab_0 - typing_extensions=4.14.1=pyhe01879c_0 @@ -409,14 +411,14 @@ dependencies: - tzlocal=5.3=py311h38be061_0 - unicodedata2=16.0.0=py311h9ecbd09_0 - urllib3=2.5.0=pyhd8ed1ab_0 - - uv=0.8.2=heb9285d_0 - - virtualenv=20.32.0=pyhd8ed1ab_0 + - uv=0.8.12=heb9285d_0 + - virtualenv=20.34.0=pyhd8ed1ab_0 - wayland=1.24.0=h3e06ad9_0 - wcwidth=0.2.13=pyhd8ed1ab_1 - webencodings=0.5.1=pyhd8ed1ab_3 - wheel=0.45.1=pyhd8ed1ab_1 - widgetsnbextension=4.0.14=pyhd8ed1ab_0 - - wrapt=1.17.2=py311h9ecbd09_0 + - wrapt=1.17.3=py311h49ec1c0_0 - xcb-util=0.4.1=h4f16b4b_2 - xcb-util-cursor=0.1.5=hb9d3cd8_0 - xcb-util-image=0.4.0=hb711507_2 @@ -443,7 +445,7 @@ dependencies: - xz=5.8.1=hbcc6ac9_2 - xz-gpl-tools=5.8.1=hbcc6ac9_2 - xz-tools=5.8.1=hb9d3cd8_2 - - yaml=0.2.5=h7f98852_2 + - yaml=0.2.5=h280c20c_3 - zeromq=4.3.5=h3b0a872_7 - zipp=3.23.0=pyhd8ed1ab_0 - zlib=1.3.1=hb9d3cd8_2 From 07e8199030f8fdde5c0d67420c3c204b519e14b0 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 19:02:04 +0800 Subject: [PATCH 03/11] Revert "Update conda lock files" This reverts commit 5ae3584e7272c88ffbd9e8031165c3ad36dbc887. --- environment-3.11-linux.yml | 169 ++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/environment-3.11-linux.yml b/environment-3.11-linux.yml index dd2ca5d8fbe..d1cfc382b47 100644 --- a/environment-3.11-linux.yml +++ b/environment-3.11-linux.yml @@ -1,7 +1,7 @@ name: sage-dev # Generated by conda-lock. # platform: linux-64 -# input_hash: a8a25255fc5cd6d8e3d469477dbce9118817fbaa8624d4c043c42427c7d864f7 +# input_hash: f2c73a276d552c1cee592849e39d4da886802f0828819a0282888079208c1f48 channels: - conda-forge @@ -27,8 +27,8 @@ dependencies: - binutils=2.44=h4852527_1 - binutils_impl_linux-64=2.44=h4bf12b8_1 - binutils_linux-64=2.44=h4852527_1 - - blas=2.134=openblas - - blas-devel=3.9.0=34_h1ea3ea9_openblas + - blas=2.132=openblas + - blas-devel=3.9.0=32_h1ea3ea9_openblas - bleach=6.2.0=pyh29332c3_4 - bleach-with-css=6.2.0=h82add2a_4 - bliss=0.77=h00ab1b0_1 @@ -41,38 +41,37 @@ dependencies: - bwidget=1.10.1=ha770c72_1 - bzip2=1.0.8=h4bc722e_7 - c-ares=1.34.5=hb9d3cd8_0 - - c-compiler=1.11.0=h4d9bdce_0 - - ca-certificates=2025.8.3=hbd8a1cb_0 + - c-compiler=1.10.0=h2b85faf_0 + - ca-certificates=2025.7.14=hbd8a1cb_0 - cachecontrol=0.14.3=pyha770c72_0 - cachecontrol-with-filecache=0.14.3=pyhd8ed1ab_0 - cairo=1.18.4=h3394656_0 - cddlib=1!0.94m=h9202a9a_0 - - certifi=2025.8.3=pyhd8ed1ab_0 + - certifi=2025.7.14=pyhd8ed1ab_0 - cffi=1.17.1=py311hf29c0ef_0 - - charset-normalizer=3.4.3=pyhd8ed1ab_0 + - charset-normalizer=3.4.2=pyhd8ed1ab_0 - click=8.2.1=pyh707e725_0 - click-default-group=1.2.4=pyhd8ed1ab_1 - cliquer=1.22=hd590300_1 - colorama=0.4.6=pyhd8ed1ab_1 - - comm=0.2.3=pyhe01879c_0 - - conda-gcc-specs=14.3.0=hb991d5c_4 + - comm=0.2.2=pyhd8ed1ab_1 - conda-lock=3.0.4=pyh367d9c9_1 - conda-souschef=2.2.3=pyhd8ed1ab_0 - - contourpy=1.3.3=py311hdf67eae_1 + - contourpy=1.3.2=py311hd18a35c_0 - conway-polynomials=0.10=pyhd8ed1ab_1 - - coverage=7.10.4=py311h3778330_0 + - coverage=7.9.2=py311h2dc5d0c_0 - cpython=3.11.13=py311hd8ed1ab_0 - crashtest=0.4.1=pyhd8ed1ab_1 - - cryptography=45.0.6=py311h8488d03_0 + - cryptography=45.0.5=py311hafd3f86_0 - curl=8.14.1=h332b0f4_0 - - cxx-compiler=1.11.0=hfcd1e18_0 + - cxx-compiler=1.10.0=h1a2810e_0 - cycler=0.12.1=pyhd8ed1ab_1 - cypari2=2.2.2=py311h8699650_0 - cyrus-sasl=2.1.28=hd9c7081_0 - cysignals=1.12.3=py311hfdbb021_0 - - cython=3.1.3=py311h91b4c63_2 + - cython=3.1.2=py311ha3e34f5_2 - dbus=1.16.2=h3c4dab8_0 - - debugpy=1.8.16=py311hc665b79_0 + - debugpy=1.8.15=py311hc665b79_0 - decorator=5.2.1=pyhd8ed1ab_0 - defusedxml=0.7.1=pyhd8ed1ab_0 - deprecated=1.2.18=pyhd8ed1ab_0 @@ -89,7 +88,7 @@ dependencies: - executing=2.2.0=pyhd8ed1ab_0 - expat=2.7.1=hecca717_0 - fflas-ffpack=2.5.0=h4f9960b_0 - - filelock=3.19.1=pyhd8ed1ab_0 + - filelock=3.18.0=pyhd8ed1ab_0 - flake8=7.3.0=pyhd8ed1ab_0 - flake8-rst-docstrings=0.3.1=pyhd8ed1ab_0 - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 @@ -99,8 +98,8 @@ dependencies: - fontconfig=2.15.0=h7e30c49_1 - fonts-conda-ecosystem=1=0 - fonts-conda-forge=1=0 - - fonttools=4.59.1=py311h3778330_0 - - fortran-compiler=1.11.0=h9bea470_0 + - fonttools=4.59.0=py311h3778330_0 + - fortran-compiler=1.10.0=h36df796_0 - fplll=5.5.0=hd20a173_0 - fpylll=0.6.3=py311hf0b6740_0 - freetype=2.13.3=ha770c72_1 @@ -108,28 +107,28 @@ dependencies: - furo=2024.8.6=pyhd8ed1ab_2 - gap-core=4.14.0=h3b03731_5 - gap-defaults=4.14.0=ha770c72_5 - - gcc=14.3.0=h76bdaa0_4 - - gcc_impl_linux-64=14.3.0=hd9e9e21_4 - - gcc_linux-64=14.3.0=h1382650_11 + - gcc=13.3.0=h9576a4e_2 + - gcc_impl_linux-64=13.3.0=h1e990d8_2 + - gcc_linux-64=13.3.0=h6f18a23_11 - gf2x=1.3.0=h55551d5_3 - gfan=0.6.2=hb86e20a_1003 - - gfortran=14.3.0=he448592_4 - - gfortran_impl_linux-64=14.3.0=h7db7018_4 - - gfortran_linux-64=14.3.0=h30a37f7_11 + - gfortran=13.3.0=h9576a4e_2 + - gfortran_impl_linux-64=13.3.0=h84c1745_2 + - gfortran_linux-64=13.3.0=h1917dac_11 - gitdb=4.0.12=pyhd8ed1ab_0 - - gitpython=3.1.45=pyhff2d567_0 + - gitpython=3.1.44=pyhff2d567_0 - givaro=4.2.0=hb397f18_2 - glpk=5.0=h445213a_0 - gmp=6.3.0=hac33072_2 - gmpy2=2.1.5=py311h0f6cedb_3 - - graphite2=1.3.14=hecca717_2 + - graphite2=1.3.14=h5888daf_0 - grayskull=2.9.1=pyhd8ed1ab_0 - gsl=2.8=hbf7d49c_1 - - gxx=14.3.0=he448592_4 - - gxx_impl_linux-64=14.3.0=he663afc_4 - - gxx_linux-64=14.3.0=ha7acb78_11 + - gxx=13.3.0=h9576a4e_2 + - gxx_impl_linux-64=13.3.0=hae580e1_2 + - gxx_linux-64=13.3.0=hb14504d_11 - h2=4.2.0=pyhd8ed1ab_0 - - harfbuzz=11.4.1=h15599e2_0 + - harfbuzz=11.3.2=hbb57e21_0 - hpack=4.1.0=pyhd8ed1ab_0 - hyperframe=6.1.0=pyhd8ed1ab_0 - icu=75.1=he02047a_0 @@ -140,17 +139,17 @@ dependencies: - importlib-metadata=8.7.0=pyhe01879c_1 - importlib_resources=6.5.2=pyhd8ed1ab_0 - iniconfig=2.0.0=pyhd8ed1ab_1 - - ipykernel=6.30.1=pyh82676e8_0 + - ipykernel=6.29.5=pyh3099207_0 - ipython=9.4.0=pyhfa0c392_0 - ipython_pygments_lexers=1.1.1=pyhd8ed1ab_0 - ipywidgets=8.1.7=pyhd8ed1ab_0 - jaraco.classes=3.4.0=pyhd8ed1ab_2 - jaraco.context=6.0.1=pyhd8ed1ab_0 - - jaraco.functools=4.3.0=pyhd8ed1ab_0 + - jaraco.functools=4.2.1=pyhd8ed1ab_0 - jedi=0.19.2=pyhd8ed1ab_1 - jeepney=0.9.0=pyhd8ed1ab_0 - jinja2=3.1.6=pyhd8ed1ab_0 - - jsonschema=4.25.1=pyhe01879c_0 + - jsonschema=4.25.0=pyhe01879c_0 - jsonschema-specifications=2025.4.1=pyh29332c3_0 - jupyter-sphinx=0.5.3=pyha770c72_5 - jupyter_client=8.6.3=pyhd8ed1ab_1 @@ -159,14 +158,14 @@ dependencies: - jupyterlab_widgets=3.0.15=pyhd8ed1ab_0 - kernel-headers_linux-64=4.18.0=he073ed8_8 - keyring=25.6.0=pyha804496_0 - - keyutils=1.6.3=hb9d3cd8_0 - - kiwisolver=1.4.9=py311h724c32c_0 + - keyutils=1.6.1=h166bdaf_0 + - kiwisolver=1.4.8=py311hd18a35c_1 - krb5=1.21.3=h659f571_0 - lcalc=2.1.0=h9cf73fc_1 - lcms2=2.17=h717163a_0 - ld_impl_linux-64=2.44=h1423503_1 - lerc=4.0.0=h0aef613_1 - - libblas=3.9.0=34_h59b9bed_openblas + - libblas=3.9.0=32_h59b9bed_openblas - libboost=1.85.0=h0ccab89_4 - libboost-devel=1.85.0=h00ab1b0_4 - libboost-headers=1.85.0=ha770c72_4 @@ -175,7 +174,7 @@ dependencies: - libbrotlicommon=1.1.0=hb9d3cd8_3 - libbrotlidec=1.1.0=hb9d3cd8_3 - libbrotlienc=1.1.0=hb9d3cd8_3 - - libcblas=3.9.0=34_he106b2a_openblas + - libcblas=3.9.0=32_he106b2a_openblas - libclang-cpp20.1=20.1.8=default_hddf928d_0 - libclang13=20.1.8=default_ha444ac7_0 - libcups=2.3.3=hb8b1518_5 @@ -190,48 +189,48 @@ dependencies: - libflint=3.2.2=h754cb6e_0 - libfreetype=2.13.3=ha770c72_1 - libfreetype6=2.13.3=h48d6fc4_1 - - libgcc=15.1.0=h767d61c_4 - - libgcc-devel_linux-64=14.3.0=h85bb3a7_104 - - libgcc-ng=15.1.0=h69a702a_4 + - libgcc=15.1.0=h767d61c_3 + - libgcc-devel_linux-64=13.3.0=hc03c837_102 + - libgcc-ng=15.1.0=h69a702a_3 - libgd=2.3.3=h6f5c62b_11 - - libgfortran=15.1.0=h69a702a_4 - - libgfortran-ng=15.1.0=h69a702a_4 - - libgfortran5=15.1.0=hcea5267_4 + - libgfortran=15.1.0=h69a702a_3 + - libgfortran-ng=15.1.0=h69a702a_3 + - libgfortran5=15.1.0=hcea5267_3 - libgl=1.7.0=ha4b6fd6_2 - - libglib=2.84.3=hf39c6af_0 + - libglib=2.84.2=h3618099_0 - libglvnd=1.7.0=ha4b6fd6_2 - libglx=1.7.0=ha4b6fd6_2 - - libgomp=15.1.0=h767d61c_4 + - libgomp=15.1.0=h767d61c_3 - libhomfly=1.02r6=hd590300_1 - - libiconv=1.18=h3b78370_2 + - libiconv=1.18=h4ce23a2_1 - libjpeg-turbo=3.1.0=hb9d3cd8_0 - - liblapack=3.9.0=34_h7ac8fdf_openblas - - liblapacke=3.9.0=34_he2f377e_openblas + - liblapack=3.9.0=32_h7ac8fdf_openblas + - liblapacke=3.9.0=32_he2f377e_openblas - libllvm20=20.1.8=hecd9e04_0 - liblzma=5.8.1=hb9d3cd8_2 - liblzma-devel=5.8.1=hb9d3cd8_2 - libnghttp2=1.64.0=h161d5f1_0 - libnsl=2.0.1=hb9d3cd8_1 - libntlm=1.8=hb9d3cd8_0 - - libopenblas=0.3.30=pthreads_h94d23a6_2 + - libopenblas=0.3.30=pthreads_h94d23a6_0 - libopengl=1.7.0=ha4b6fd6_2 - libpciaccess=0.18=hb9d3cd8_0 - - libpng=1.6.50=h421ea60_1 - - libpq=17.6=h3675c94_0 - - libsanitizer=14.3.0=hd08acf3_4 + - libpng=1.6.50=h943b412_0 + - libpq=17.5=h27ae623_0 + - libsanitizer=13.3.0=he8ea267_2 - libsodium=1.0.20=h4ab18f5_0 - - libsqlite=3.50.4=h0c1763c_0 + - libsqlite=3.50.3=hee844dc_1 - libssh2=1.11.1=hcf80075_0 - - libstdcxx=15.1.0=h8f9b012_4 - - libstdcxx-devel_linux-64=14.3.0=h85bb3a7_104 - - libstdcxx-ng=15.1.0=h4852527_4 - - libtiff=4.7.0=h8261f1e_6 + - libstdcxx=15.1.0=h8f9b012_3 + - libstdcxx-devel_linux-64=13.3.0=hc03c837_102 + - libstdcxx-ng=15.1.0=h4852527_3 + - libtiff=4.7.0=hf01ce69_5 - libuuid=2.38.1=h0b41bf4_0 - libwebp-base=1.6.0=hd42ef1d_0 - libxcb=1.17.0=h8a09558_0 - libxcrypt=4.4.36=hd590300_1 - - libxkbcommon=1.11.0=he8b52b9_0 - - libxml2=2.13.8=h04c0eec_1 + - libxkbcommon=1.10.0=h65c71a3_0 + - libxml2=2.13.8=h4bc477f_0 - libxslt=1.1.43=h7a3aeb2_0 - libzlib=1.3.1=hb9d3cd8_2 - linbox=1.7.0=h0451620_2 @@ -242,13 +241,13 @@ dependencies: - m4rie=20200125=h051dbe0_0 - make=4.4.1=hb9d3cd8_2 - markupsafe=3.0.2=py311h2dc5d0c_1 - - matplotlib=3.10.5=py311h38be061_0 - - matplotlib-base=3.10.5=py311h0f3be63_0 + - matplotlib=3.10.3=py311h38be061_0 + - matplotlib-base=3.10.3=py311h2b939e6_0 - matplotlib-inline=0.1.7=pyhd8ed1ab_1 - maxima=5.47.0=h75482ee_3 - mccabe=0.7.0=pyhd8ed1ab_1 - memory-allocator=0.1.3=py311h9ecbd09_1 - - meson=1.8.3=pyhe01879c_0 + - meson=1.8.2=pyhe01879c_0 - meson-python=0.18.0=pyh70fd9c4_0 - mistune=3.1.3=pyh29332c3_0 - more-itertools=10.7.0=pyhd8ed1ab_0 @@ -269,11 +268,11 @@ dependencies: - networkx=3.5=pyhe01879c_0 - ninja=1.13.1=h171cf75_0 - ntl=11.4.3=hef3c4d3_1 - - numpy=2.3.2=py311h2e04523_0 - - openblas=0.3.30=pthreads_h6ec200e_2 - - openjpeg=2.5.3=h55fea9a_1 + - numpy=2.3.1=py311h2e04523_1 + - openblas=0.3.30=pthreads_h6ec200e_0 + - openjpeg=2.5.3=h5fbd93e_0 - openldap=2.6.10=he970967_0 - - openssl=3.5.2=h26f9b46_0 + - openssl=3.5.1=h7b32b05_0 - packaging=25.0=pyh29332c3_1 - palp=2.20=h36c2ea0_0 - pandoc=3.7.0.2=ha770c72_0 @@ -290,8 +289,8 @@ dependencies: - pexpect=4.9.0=pyhd8ed1ab_1 - pickleshare=0.7.5=pyhd8ed1ab_1004 - pillow=11.3.0=py311h1322bbf_0 - - pip=25.2=pyh8b19718_0 - - pixman=0.46.4=h54a6638_1 + - pip=25.1.1=pyh8b19718_0 + - pixman=0.46.4=h537e5f6_0 - pkg-config=0.29.2=h4bc722e_1009 - pkgconfig=1.5.5=pyhd8ed1ab_5 - pkginfo=1.12.1.2=pyhd8ed1ab_0 @@ -315,11 +314,11 @@ dependencies: - pydantic=2.11.7=pyh3cfb1c2_0 - pydantic-core=2.33.2=py311hdae7d1d_0 - pyflakes=3.4.0=pyhd8ed1ab_0 - - pygithub=2.7.0=pyhd8ed1ab_0 + - pygithub=2.6.1=pyhd8ed1ab_0 - pygments=2.19.2=pyhd8ed1ab_0 - pyjwt=2.10.1=pyhd8ed1ab_0 - pynacl=1.5.0=py311h9ecbd09_4 - - pyparsing=3.2.3=pyhe01879c_2 + - pyparsing=3.2.3=pyhd8ed1ab_1 - pyproject-metadata=0.9.1=pyhd8ed1ab_0 - pyproject_hooks=1.2.0=pyhd8ed1ab_1 - pyside6=6.9.1=py311h846acb3_0 @@ -327,9 +326,9 @@ dependencies: - pytest=8.4.1=pyhd8ed1ab_0 - pytest-xdist=3.8.0=pyhd8ed1ab_0 - python=3.11.13=h9e4cc4f_0_cpython - - python-build=1.3.0=pyhff2d567_0 + - python-build=1.2.2.post1=pyhff2d567_1 - python-dateutil=2.9.0.post0=pyhe01879c_2 - - python-fastjsonschema=2.21.2=pyhe01879c_0 + - python-fastjsonschema=2.21.1=pyhd8ed1ab_0 - python-installer=0.7.0=pyhff2d567_1 - python-lrcalc=2.1=py311hfdbb021_7 - python-symengine=0.14.0=py311h7b351a7_1 @@ -337,10 +336,10 @@ dependencies: - python_abi=3.11=8_cp311 - pytz=2025.2=pyhd8ed1ab_0 - pyyaml=6.0.2=py311h2dc5d0c_2 - - pyzmq=27.0.1=py311hc251a9f_0 + - pyzmq=27.0.0=py311h7deb3e3_0 - qd=2.3.22=h2cc385e_1004 - qhull=2020.2=h434a139_5 - - qt6-main=6.9.1=h6ac528c_2 + - qt6-main=6.9.1=h0384650_1 - r-base=4.2.3=h2d83a99_19 - r-lattice=0.22_6=r42h57805ef_0 - rapidfuzz=3.13.0=py311hfdbb021_0 @@ -350,17 +349,17 @@ dependencies: - requests-toolbelt=1.0.0=pyhd8ed1ab_1 - restructuredtext_lint=1.4.0=pyhd8ed1ab_1 - roman-numerals-py=3.1.0=pyhd8ed1ab_0 - - rpds-py=0.27.0=py311h902ca64_0 + - rpds-py=0.26.0=py311hdae7d1d_0 - rpy2=3.5.11=py311r42h1f0f07a_3 - ruamel.yaml=0.18.14=py311h9ecbd09_0 - ruamel.yaml.clib=0.2.8=py311h9ecbd09_1 - ruamel.yaml.jinja2=0.2.7=pyhd8ed1ab_1 - - ruff=0.12.9=hbf64f1c_1 + - ruff=0.12.4=hf9daec2_0 - rw=0.9=hd590300_2 - sagemath-db-elliptic-curves=0.8.1=hecc5488_0 - sagemath-db-graphs=20210214=hd8ed1ab_0 - sagemath-db-polytopes=20170220=1 - - scipy=1.16.1=py311h33d6a90_0 + - scipy=1.16.0=py311h2d3ef60_0 - secretstorage=3.3.3=py311h38be061_3 - sed=4.9=h6688a6e_0 - semver=3.0.4=pyhd8ed1ab_0 @@ -384,7 +383,7 @@ dependencies: - sphinxcontrib-jsmath=1.0.1=pyhd8ed1ab_1 - sphinxcontrib-qthelp=2.0.0=pyhd8ed1ab_1 - sphinxcontrib-serializinghtml=1.1.10=pyhd8ed1ab_1 - - sqlite=3.50.4=hbc0de68_0 + - sqlite=3.50.3=heff268d_1 - stack_data=0.6.3=pyhd8ed1ab_1 - stdlib-list=0.11.1=pyhd8ed1ab_0 - symengine=0.14.0=h064106a_1 @@ -397,13 +396,13 @@ dependencies: - tk=8.6.13=noxft_hd72426e_102 - tktable=2.10=h8d826fa_7 - toml=0.10.2=pyhd8ed1ab_1 - - tomli=2.2.1=pyhe01879c_2 + - tomli=2.2.1=pyhd8ed1ab_1 - tomli-w=1.2.0=pyhd8ed1ab_0 - tomlkit=0.13.3=pyha770c72_0 - - tornado=6.5.2=py311h49ec1c0_0 + - tornado=6.5.1=py311h9ecbd09_0 - tqdm=4.67.1=pyhd8ed1ab_1 - traitlets=5.14.3=pyhd8ed1ab_1 - - trove-classifiers=2025.8.6.13=pyhd8ed1ab_0 + - trove-classifiers=2025.5.9.12=pyhd8ed1ab_0 - typing-extensions=4.14.1=h4440ef1_0 - typing-inspection=0.4.1=pyhd8ed1ab_0 - typing_extensions=4.14.1=pyhe01879c_0 @@ -411,14 +410,14 @@ dependencies: - tzlocal=5.3=py311h38be061_0 - unicodedata2=16.0.0=py311h9ecbd09_0 - urllib3=2.5.0=pyhd8ed1ab_0 - - uv=0.8.12=heb9285d_0 - - virtualenv=20.34.0=pyhd8ed1ab_0 + - uv=0.8.2=heb9285d_0 + - virtualenv=20.32.0=pyhd8ed1ab_0 - wayland=1.24.0=h3e06ad9_0 - wcwidth=0.2.13=pyhd8ed1ab_1 - webencodings=0.5.1=pyhd8ed1ab_3 - wheel=0.45.1=pyhd8ed1ab_1 - widgetsnbextension=4.0.14=pyhd8ed1ab_0 - - wrapt=1.17.3=py311h49ec1c0_0 + - wrapt=1.17.2=py311h9ecbd09_0 - xcb-util=0.4.1=h4f16b4b_2 - xcb-util-cursor=0.1.5=hb9d3cd8_0 - xcb-util-image=0.4.0=hb711507_2 @@ -445,7 +444,7 @@ dependencies: - xz=5.8.1=hbcc6ac9_2 - xz-gpl-tools=5.8.1=hbcc6ac9_2 - xz-tools=5.8.1=hb9d3cd8_2 - - yaml=0.2.5=h280c20c_3 + - yaml=0.2.5=h7f98852_2 - zeromq=4.3.5=h3b0a872_7 - zipp=3.23.0=pyhd8ed1ab_0 - zlib=1.3.1=hb9d3cd8_2 From 740ae47b392483e69f05655facfe42b17085b789 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 20:41:15 +0800 Subject: [PATCH 04/11] Make syntax compatible with Python 3.11 --- src/sage_docbuild/ext/sage_transformer.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage_docbuild/ext/sage_transformer.py b/src/sage_docbuild/ext/sage_transformer.py index 27bcbdad8a7..9bba77b3dd3 100644 --- a/src/sage_docbuild/ext/sage_transformer.py +++ b/src/sage_docbuild/ext/sage_transformer.py @@ -1,5 +1,5 @@ """Classes for docstring parsing and formatting.""" -# https://github.com/sphinx-doc/sphinx/tree/master/sphinx/ext/napoleon +# Heavily inspired by https://github.com/sphinx-doc/sphinx/tree/master/sphinx/ext/napoleon from __future__ import annotations @@ -14,7 +14,7 @@ if TYPE_CHECKING: from collections.abc import Callable - +from typing import TypeVar logger = logging.getLogger(__name__) @@ -38,7 +38,10 @@ ) -class Deque[T](collections.deque[T]): +T = TypeVar("T") + + +class Deque(collections.deque[T]): """A subclass of deque that mimics ``pockets.iterators.modify_iter``. The `.Deque.get` and `.Deque.next` methods are added. @@ -50,7 +53,7 @@ def get(self, n: int) -> T: """Return the nth element of the stack, or ``self.sentinel`` if n is greater than the stack size. """ - return self[n] if n < len(self) else self.sentinel + return self[n] if n < len(self) else self.sentinel # type: ignore[return-value] def next(self) -> T: if self: @@ -165,7 +168,8 @@ def _consume_fields(self, multiple: bool = False) -> list[tuple[str, list[str]]] _name, _desc = self._consume_field() if multiple and _name: fields.extend( - (name.strip().strip(r"`").strip(), _desc) for name in _name.split(",") + (name.strip().strip(r"`").strip(), _desc) + for name in _name.split(",") ) elif _name or _desc: fields.append((_name.strip().strip(r"`").strip(), _desc)) From a5fa8c3ac95b351e807173501bf0e66d1c1144cc Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 22:55:28 +0800 Subject: [PATCH 05/11] Fix various syntax errors in docstrings --- build/sage_bootstrap/tarball.py | 2 +- .../algebras/fusion_rings/fusion_double.py | 2 +- src/sage/arith/misc.py | 6 +-- src/sage/calculus/desolvers.py | 20 +++++----- src/sage/categories/affine_weyl_groups.py | 2 +- src/sage/categories/fields.py | 2 +- src/sage/categories/magmas.py | 1 + src/sage/categories/rings.py | 2 +- src/sage/coding/guruswami_sudan/utils.py | 2 +- src/sage/coding/linear_code_no_metric.py | 2 +- src/sage/coding/source_coding/huffman.py | 8 ---- src/sage/combinat/bijectionist.py | 2 +- src/sage/combinat/combinat.py | 4 +- src/sage/combinat/finite_state_machine.py | 7 ++-- src/sage/databases/cubic_hecke_db.py | 1 + src/sage/databases/sql_db.py | 11 ++++-- .../arithmetic_dynamics/projective_ds.py | 8 ++-- src/sage/games/sudoku.py | 12 +++--- .../hyperbolic_space/hyperbolic_isometry.py | 2 +- src/sage/geometry/point_collection.pyx | 17 ++++---- .../combinatorial_polyhedron/base.pyx | 39 +++++++++++-------- src/sage/graphs/digraph.py | 4 +- src/sage/graphs/digraph_generators.py | 11 +++--- src/sage/groups/free_group.py | 2 +- src/sage/logic/boolformula.py | 2 +- src/sage/manifolds/chart.py | 2 + src/sage/misc/defaults.py | 4 +- src/sage/misc/function_mangling.pyx | 2 +- src/sage/modular/abvar/abvar.py | 2 +- .../modular/arithgroup/congroup_gammaH.py | 11 +++--- .../modular/btquotients/pautomorphicform.py | 4 +- src/sage/modules/free_module.py | 4 +- .../numerical/backends/cvxopt_backend.pyx | 2 +- .../backends/generic_sdp_backend.pyx | 2 +- src/sage/numerical/backends/glpk_backend.pyx | 2 +- .../numerical/backends/matrix_sdp_backend.pyx | 2 +- src/sage/numerical/backends/scip_backend.pyx | 2 +- src/sage/parallel/decorate.py | 2 +- src/sage/schemes/curves/projective_curve.py | 2 +- src/sage/schemes/toric/divisor.py | 2 +- src/sage/sets/cartesian_product.py | 1 + src/sage/stats/hmm/distributions.pyx | 2 +- src/sage/structure/indexed_generators.py | 34 ++++++++-------- src/sage/topology/cell_complex.py | 23 ++++++++--- 44 files changed, 149 insertions(+), 127 deletions(-) diff --git a/build/sage_bootstrap/tarball.py b/build/sage_bootstrap/tarball.py index 54af50dd8f8..e4bb91bf55d 100644 --- a/build/sage_bootstrap/tarball.py +++ b/build/sage_bootstrap/tarball.py @@ -50,7 +50,7 @@ def __init__(self, tarball_name, package=None): INPUT: - - ``tarball_name`` - string. The full filename (``foo-1.3.tar.bz2``) + - ``tarball_name`` -- string. The full filename (``foo-1.3.tar.bz2``) of a tarball on the Sage mirror network. """ self.__filename = tarball_name diff --git a/src/sage/algebras/fusion_rings/fusion_double.py b/src/sage/algebras/fusion_rings/fusion_double.py index 76595ccfc91..f04ab21105c 100644 --- a/src/sage/algebras/fusion_rings/fusion_double.py +++ b/src/sage/algebras/fusion_rings/fusion_double.py @@ -243,7 +243,7 @@ def s_ij(self, i, j, unitary=False, base_coercion=True): INPUT: - - ``i``, ``j``, -- a pair of basis elements + - ``i``, ``j`` -- a pair of basis elements - ``unitary`` -- boolean (default: ``False``); set to ``True`` to obtain the unitary `S`-matrix diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index f0ca76fe42d..0cb79f03919 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -284,7 +284,7 @@ def bernoulli(n, algorithm='default', num_threads=1): INPUT: - ``n`` -- integer - - ``algorithm``: + - ``algorithm`` -- one of the following: - ``'default'`` -- use 'flint' for n <= 20000, then 'arb' for n <= 300000 and 'bernmm' for larger values (this is just a heuristic, and not guaranteed @@ -4052,8 +4052,8 @@ def multinomial(*ks): INPUT: - - either an arbitrary number of integer arguments `k_1,\dots,k_n` - - or an iterable (e.g. a list) of integers `[k_1,\dots,k_n]` + - ``*ks`` -- either an arbitrary number of integer arguments `k_1,\dots,k_n` + or an iterable (e.g. a list) of integers `[k_1,\dots,k_n]` OUTPUT: the integer: diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index eb166948087..bbc7d21bc0e 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -1511,7 +1511,7 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() :func:`~scipy:scipy.integrate.odeint` function from :mod:`scipy:scipy.integrate`): - - ``rtol``, ``atol`` : float + - ``rtol``, ``atol`` -- float The input parameters ``rtol`` and ``atol`` determine the error control performed by the solver. The solver will control the vector, `e`, of estimated local errors in `y`, according to an @@ -1525,33 +1525,33 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() ``rtol`` and ``atol`` can be either vectors the same length as `y` or scalars. - - ``tcrit`` : array + - ``tcrit`` -- array Vector of critical points (e.g. singularities) where integration care should be taken. - - ``h0`` : float, (0: solver-determined) + - ``h0`` -- float, (0: solver-determined) The step size to be attempted on the first step. - - ``hmax`` : float, (0: solver-determined) + - ``hmax`` -- float, (0: solver-determined) The maximum absolute step size allowed. - - ``hmin`` : float, (0: solver-determined) + - ``hmin`` -- float, (0: solver-determined) The minimum absolute step size allowed. - - ``ixpr`` : boolean. + - ``ixpr`` -- boolean. Whether to generate extra printing at method switches. - - ``mxstep`` : integer, (0: solver-determined) + - ``mxstep`` -- integer, (0: solver-determined) Maximum number of (internally defined) steps allowed for each integration point in t. - - ``mxhnil`` : integer, (0: solver-determined) + - ``mxhnil`` -- integer, (0: solver-determined) Maximum number of messages printed. - - ``mxordn`` : integer, (0: solver-determined) + - ``mxordn`` -- integer, (0: solver-determined) Maximum order to be allowed for the nonstiff (Adams) method. - - ``mxords`` : integer, (0: solver-determined) + - ``mxords`` -- integer, (0: solver-determined) Maximum order to be allowed for the stiff (BDF) method. OUTPUT: a list with the solution of the system at each time in ``times`` diff --git a/src/sage/categories/affine_weyl_groups.py b/src/sage/categories/affine_weyl_groups.py index b904c96319e..051581bb4c6 100644 --- a/src/sage/categories/affine_weyl_groups.py +++ b/src/sage/categories/affine_weyl_groups.py @@ -218,7 +218,7 @@ def affine_grassmannian_to_partition(self): INPUT: - - ``self`` is affine Grassmannian element of the affine Weyl group of type `A_k^{(1)}` (i.e. all reduced words end in 0) + - ``self`` -- an affine Grassmannian element of the affine Weyl group of type `A_k^{(1)}` (i.e. all reduced words end in 0) OUTPUT: `k`-bounded partition diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index f8536e43154..dfce8ac368c 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -636,7 +636,7 @@ def ideal(self, *gens, **kwds): INPUT: - - an element or a list/tuple/sequence of elements, the generators + - ``gens`` -- an element or a list/tuple/sequence of elements, the generators Any named arguments are ignored. diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index c9119a88544..b6d83fb25c2 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -865,6 +865,7 @@ def multiplication_table(self, names='letters', elements=None): of the elements themselves. * a list - a list of strings, where the length of the list equals the number of elements. + - ``elements`` -- (default: ``None``) a list of elements of the magma, in forms that can be coerced into the structure, eg. their string diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 28872b12b26..70bb886d3e7 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -981,7 +981,7 @@ def ideal(self, *args, **kwds): INPUT: - - an element or a list/tuple/sequence of elements, the generators + - ``args`` -- an element or a list/tuple/sequence of elements, the generators - ``coerce`` -- boolean (default: ``True``); whether to first coerce the elements into this ring. This must be a keyword diff --git a/src/sage/coding/guruswami_sudan/utils.py b/src/sage/coding/guruswami_sudan/utils.py index 320c07c6c52..6668ab464b2 100644 --- a/src/sage/coding/guruswami_sudan/utils.py +++ b/src/sage/coding/guruswami_sudan/utils.py @@ -116,7 +116,7 @@ def solve_degree2_to_integer_range(a, b, c): INPUT: - - ``a``, ``b`` and ``c`` -- coefficients of a second degree equation, ``a`` + - ``a``, ``b``, ``c`` -- coefficients of a second degree equation, ``a`` being the coefficient of the higher degree term EXAMPLES:: diff --git a/src/sage/coding/linear_code_no_metric.py b/src/sage/coding/linear_code_no_metric.py index 0845e19dee8..692fc015a00 100644 --- a/src/sage/coding/linear_code_no_metric.py +++ b/src/sage/coding/linear_code_no_metric.py @@ -671,7 +671,7 @@ def is_information_set(self, positions): INPUT: - - A list of positions, i.e. integers in the range 0 to `n-1` where `n` + - ``positions`` -- list of positions, i.e. integers in the range 0 to `n-1` where `n` is the length of ``self``. OUTPUT: boolean indicating whether the positions form an information set diff --git a/src/sage/coding/source_coding/huffman.py b/src/sage/coding/source_coding/huffman.py index 2755e033632..6dcaf7669e3 100644 --- a/src/sage/coding/source_coding/huffman.py +++ b/src/sage/coding/source_coding/huffman.py @@ -451,10 +451,6 @@ def encoding_table(self): r""" Return the current encoding table. - INPUT: - - - None. - OUTPUT: a dictionary associating an alphabetic symbol to a Huffman encoding EXAMPLES:: @@ -492,10 +488,6 @@ def tree(self): r""" Return the Huffman tree corresponding to the current encoding. - INPUT: - - - None. - OUTPUT: the binary tree representing a Huffman code EXAMPLES:: diff --git a/src/sage/combinat/bijectionist.py b/src/sage/combinat/bijectionist.py index 1404d9cc86b..45773d6777c 100644 --- a/src/sage/combinat/bijectionist.py +++ b/src/sage/combinat/bijectionist.py @@ -1107,7 +1107,7 @@ def set_distributions(self, *elements_distributions): INPUT: - - one or more pairs of `(\tilde A, \tilde Z)`, where `\tilde + - ``elements_distributions`` -- one or more pairs of `(\tilde A, \tilde Z)`, where `\tilde A\subseteq A` and `\tilde Z` is a list of values in `Z` of the same size as `\tilde A` diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index ff009cf3c66..aea679c88ef 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -806,7 +806,7 @@ def stirling_number1(n, k, algorithm='gap') -> Integer: - ``n`` -- nonnegative machine-size integer - ``k`` -- nonnegative machine-size integer - - ``algorithm``: + - ``algorithm`` -- one of the following: * ``'gap'`` -- default; use GAP's ``Stirling1`` function * ``'flint'`` -- use flint's ``arith_stirling_number_1u`` function @@ -864,7 +864,7 @@ def stirling_number2(n, k, algorithm=None) -> Integer: - ``n`` -- nonnegative machine-size integer - ``k`` -- nonnegative machine-size integer - - ``algorithm``: + - ``algorithm`` -- one of the following: * ``None`` -- default; use native implementation * ``'flint'`` -- use flint's ``arith_stirling_number_2`` function diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 96809a71dea..c52093b1f94 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -9398,9 +9398,10 @@ def graph(self, edge_labels='words_in_out'): INPUT: - ``edge_label`` -- (default: ``'words_in_out'``) can be - - ``'words_in_out'`` (labels will be strings ``'i|o'``) - - a function with which takes as input a transition - and outputs (returns) the label + + - ``'words_in_out'`` (labels will be strings ``'i|o'``) + - a function with which takes as input a transition + and outputs (returns) the label OUTPUT: a :class:`directed graph ` diff --git a/src/sage/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py index 1bf6e6a729c..3839cfcf196 100644 --- a/src/sage/databases/cubic_hecke_db.py +++ b/src/sage/databases/cubic_hecke_db.py @@ -929,6 +929,7 @@ def read_matrix_representation(self, representation_type, monomial_tietze, ring_ r""" Return the matrix representations of the given monomial (in Tietze form) if it has been stored in the file cache before. + INPUT: - ``representation_type`` -- an element of diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index 3ee328c6cf9..4ef7e2ae0dc 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -75,9 +75,10 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import sqlite3 as sqlite import os import re +import sqlite3 as sqlite + from sage.misc.temporary_file import tmp_filename from sage.structure.sage_object import SageObject @@ -413,7 +414,7 @@ def __init__(self, database, *args, **kwds): - ``query_dict`` -- dictionary specifying the query itself. The format is:: - {'table_name':'tblname', 'display_cols':['col1', 'col2','col3'], 'expression': [col, operator, value]} + {'table_name':'tblname', 'display_cols':['col1', 'col2','col3'], 'expression': [col, operator, value]} NOTE: Every SQL type we are using is ultimately represented as a string, @@ -720,7 +721,7 @@ def intersect(self, other, join_table=None, join_dict=None, the new query. (Must include a mapping for all tables, including those previously joined in either query). Structure is given by:: - {'join_table1':('corr_base_col1', 'col1'), 'join_table2':('corr_base_col2', 'col2')} + {'join_table1':('corr_base_col1', 'col1'), 'join_table2':('corr_base_col2', 'col2')} where ``join_table1`` is to be joined with ``join_table`` on ``join_table.corr_base_col1 = join_table1.col1`` @@ -848,11 +849,13 @@ def union(self, other, join_table=None, join_dict=None, in_place=False): INPUT: - ``other`` -- the ``SQLQuery`` to union with + - ``join_table`` -- base table to join on (This table should have at least one column in each table to join on). + - ``join_dict`` -- dictionary that represents the join structure for the new query. (Must include a mapping for all tables, including - those previously joined in either query). Structure is given by:: + those previously joined in either query). Structure is given by: {'join_table1':('corr_base_col1', 'col1'), 'join_table2':('corr_base_col2', 'col2')} diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 0b72702aa96..80b9f00cdfa 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -6845,10 +6845,10 @@ def Lattes_to_curve(self, return_conjugation=False, check_lattes=False): INPUT: - ``return_conjugation`` -- (default: ``False``) if ``True``, then - return the conjugation that moves self to a map that comes from a - Short Weierstrass Model Elliptic curve - ``check_lattes`` -- (default: ``False``) if ``True``, then will ValueError if not Lattes + - ``return_conjugation`` -- (default: ``False``) if ``True``, then + return the conjugation that moves self to a map that comes from a + Short Weierstrass Model Elliptic curve + - ``check_lattes`` -- (default: ``False``) if ``True``, then will ValueError if not Lattes OUTPUT: a Short Weierstrass Model Elliptic curve which is isogenous to the Elliptic curve of 'self', diff --git a/src/sage/games/sudoku.py b/src/sage/games/sudoku.py index a500fef808e..6c811002cfc 100644 --- a/src/sage/games/sudoku.py +++ b/src/sage/games/sudoku.py @@ -92,11 +92,13 @@ class Sudoku(SageObject): INPUT: - ``puzzle`` -- the first argument can take one of three forms - * list - a Python list with elements of the puzzle in row-major order, - where a blank entry is a zero - * matrix - a square Sage matrix over `\ZZ` - * string - a string where each character is an entry of - the puzzle. For two-digit entries, a = 10, b = 11, etc. + + * list - a Python list with elements of the puzzle in row-major order, + where a blank entry is a zero + * matrix - a square Sage matrix over `\ZZ` + * string - a string where each character is an entry of + the puzzle. For two-digit entries, a = 10, b = 11, etc. + - ``verify_input`` -- boolean (default: ``True``); use ``False`` if you know the input is valid diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py index 9fe8f9e5a67..bd893eddaed 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -991,7 +991,7 @@ class HyperbolicIsometryKM(HyperbolicIsometry): INPUT: - - a matrix in `SO(2,1)` + - ``p`` -- a matrix in `SO(2,1)` EXAMPLES:: diff --git a/src/sage/geometry/point_collection.pyx b/src/sage/geometry/point_collection.pyx index 8cebd2fd2d8..01bbd1ece93 100644 --- a/src/sage/geometry/point_collection.pyx +++ b/src/sage/geometry/point_collection.pyx @@ -790,14 +790,15 @@ cdef class PointCollection(SageObject): INPUT: - ``format`` -- (optional) if given, must be one of the strings - * "default" -- output one point per line with vertical alignment of - coordinates in text mode, same as "tuple" for LaTeX; - * "tuple" -- output ``tuple(self)`` with lattice information; - * "matrix" -- output :meth:`matrix` with lattice information; - * "column matrix" -- output :meth:`column_matrix` with lattice - information; - * "separated column matrix" -- same as "column matrix" for text - mode, for LaTeX separate columns by lines (not shown by jsMath). + + * "default" -- output one point per line with vertical alignment of + coordinates in text mode, same as "tuple" for LaTeX; + * "tuple" -- output ``tuple(self)`` with lattice information; + * "matrix" -- output :meth:`matrix` with lattice information; + * "column matrix" -- output :meth:`column_matrix` with lattice + information; + * "separated column matrix" -- same as "column matrix" for text + mode, for LaTeX separate columns by lines (not shown by jsMath). OUTPUT: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 8c8e7c49a38..4aeeaedd8c1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -118,32 +118,37 @@ cdef class CombinatorialPolyhedron(SageObject): INPUT: - ``data`` -- an instance of - * :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` - * or a :class:`~sage.geometry.lattice_polytope.LatticePolytopeClass` - * or a :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` - * or an ``incidence_matrix`` as in - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix` - In this case you should also specify the ``Vrep`` and ``facets`` arguments - * or list of facets, each facet given as - a list of ``[vertices, rays, lines]`` if the polyhedron is unbounded, - then rays and lines and the extra argument ``nr_lines`` are required - if the polyhedron contains no lines, the rays can be thought of - as the vertices of the facets deleted from a bounded polyhedron see - :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` on how to use - rays and lines - * or an integer, representing the dimension of a polyhedron equal to its - affine hull - * or a tuple consisting of facets and vertices as two - :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. + + * :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` + * or a :class:`~sage.geometry.lattice_polytope.LatticePolytopeClass` + * or a :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` + * or an ``incidence_matrix`` as in + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.incidence_matrix` + In this case you should also specify the ``Vrep`` and ``facets`` arguments + * or list of facets, each facet given as + a list of ``[vertices, rays, lines]`` if the polyhedron is unbounded, + then rays and lines and the extra argument ``nr_lines`` are required + if the polyhedron contains no lines, the rays can be thought of + as the vertices of the facets deleted from a bounded polyhedron see + :class:`~sage.geometry.polyhedron.parent.Polyhedron_base` on how to use + rays and lines + * or an integer, representing the dimension of a polyhedron equal to its + affine hull + * or a tuple consisting of facets and vertices as two + :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. + - ``Vrep`` -- (optional) when ``data`` is an incidence matrix, it should be the list of ``[vertices, rays, lines]``, if the rows in the incidence_matrix should correspond to names + - ``facets`` -- (optional) when ``data`` is an incidence matrix or a list of facets, it should be a list of facets that would be used instead of indices (of the columns of the incidence matrix). + - ``unbounded`` -- value will be overwritten if ``data`` is a polyhedron; if ``unbounded`` and ``data`` is incidence matrix or a list of facets, need to specify ``far_face`` + - ``far_face`` -- (semi-optional); if the polyhedron is unbounded this needs to be set to the list of indices of the rays and line unless ``data`` is an instance of :class:`~sage.geometry.polyhedron.parent.Polyhedron_base`. diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py index 8ce576d9b26..34d08c7e629 100644 --- a/src/sage/graphs/digraph.py +++ b/src/sage/graphs/digraph.py @@ -193,12 +193,12 @@ class DiGraph(GenericGraph): of :mod:`~sage.graphs.digraph`, :mod:`~sage.graphs.generic_graph`, and :mod:`~sage.graphs.graph`. - INPUT: - By default, a :class:`DiGraph` object is simple (i.e. no *loops* nor *multiple edges*) and unweighted. This can be easily tuned with the appropriate flags (see below). + INPUT: + - ``data`` -- can be any of the following (see the ``format`` argument): #. ``DiGraph()`` -- build a digraph on 0 vertices diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 820131745fa..7462c03bd05 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -1023,11 +1023,12 @@ def DeBruijn(self, k, n, vertices='strings', immutable=False): INPUT: - - ``k`` -- two possibilities for this parameter : - - An integer equal to the cardinality of the alphabet to use, that - is, the degree of the digraph to be produced. - - An iterable object to be used as the set of letters. The degree - of the resulting digraph is the cardinality of the set of letters. + - ``k`` -- two possibilities for this parameter: + + - An integer equal to the cardinality of the alphabet to use, that + is, the degree of the digraph to be produced. + - An iterable object to be used as the set of letters. The degree + of the resulting digraph is the cardinality of the set of letters. - ``n`` -- integer; length of words in the De Bruijn digraph when ``vertices == 'strings'``, and also the diameter of the digraph diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 2324d81538e..85b81a0562b 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -905,7 +905,7 @@ def quotient(self, relations, **kwds): - ``relations`` -- list/tuple/iterable with the elements of the free group - - further named arguments, that are passed to the constructor + - ``kwds`` -- further named arguments, that are passed to the constructor of a finitely presented group OUTPUT: diff --git a/src/sage/logic/boolformula.py b/src/sage/logic/boolformula.py index b496bb91158..04a910d7b19 100644 --- a/src/sage/logic/boolformula.py +++ b/src/sage/logic/boolformula.py @@ -1266,7 +1266,7 @@ def dist_not(self, tree): INPUT: - - ``tree`` a list; this represents a branch + - ``tree`` -- a list; this represents a branch of a parse tree OUTPUT: a new list diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index c9bc4c802f1..f052c2faeb2 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -92,6 +92,7 @@ class Chart(UniqueRepresentation, SageObject): - ``None``: the default of :class:`~sage.manifolds.calculus_method.CalculusMethod` will be used + - ``names`` -- (default: ``None``) unused argument, except if ``coordinates`` is not provided; it must then be a tuple containing the coordinate symbols (this is guaranteed if the shortcut operator @@ -1662,6 +1663,7 @@ class RealChart(Chart): - ``None``: the default of :class:`~sage.manifolds.calculus_method.CalculusMethod` will be used + - ``names`` -- (default: ``None``) unused argument, except if ``coordinates`` is not provided; it must then be a tuple containing the coordinate symbols (this is guaranteed if the shortcut operator diff --git a/src/sage/misc/defaults.py b/src/sage/misc/defaults.py index ea2719a2bf6..39f24698a41 100644 --- a/src/sage/misc/defaults.py +++ b/src/sage/misc/defaults.py @@ -31,7 +31,7 @@ def variable_names(n, name=None): - ``n`` -- a nonnegative Integer; the number of variable names to output - - ``names`` a string (default: ``None``); the root of the variable + - ``names`` -- a string (default: ``None``); the root of the variable name EXAMPLES:: @@ -63,7 +63,7 @@ def latex_variable_names(n, name=None): - ``n`` -- a nonnegative Integer; the number of variable names to output - - ``names`` a string (default: ``None``); the root of the variable + - ``names`` -- string (default: ``None``); the root of the variable name EXAMPLES:: diff --git a/src/sage/misc/function_mangling.pyx b/src/sage/misc/function_mangling.pyx index 7a212de9901..72d161e54f5 100644 --- a/src/sage/misc/function_mangling.pyx +++ b/src/sage/misc/function_mangling.pyx @@ -165,7 +165,7 @@ cdef class ArgumentFixer: INPUT: - - any positional and named arguments. + - ``args``, ``kwargs`` -- any positional and named arguments. OUTPUT: we return a tuple diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 6f1f9a72e1b..6feca0da000 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -1181,7 +1181,7 @@ def quotient(self, other, **kwds): INPUT: - ``other`` -- a finite subgroup or subvariety - - further named arguments, that are currently ignored + - ``kwds`` -- further named arguments, that are currently ignored OUTPUT: a pair (A, phi) with phi the quotient map from ``self`` to A diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index 8617c280c45..63c9176cafc 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -47,11 +47,12 @@ def GammaH_constructor(level, H): - ``level`` -- integer - ``H`` -- either 0, 1, or a list - * If H is a list, return `\Gamma_H(N)`, where `H` - is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the - elements of the list. - * If H = 0, returns `\Gamma_0(N)`. - * If H = 1, returns `\Gamma_1(N)`. + + * If H is a list, return `\Gamma_H(N)`, where `H` + is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the + elements of the list. + * If H = 0, returns `\Gamma_0(N)`. + * If H = 1, returns `\Gamma_1(N)`. EXAMPLES:: diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 3b61759f6f1..eb2e7da8c1a 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -189,8 +189,8 @@ def __init__(self, _parent, vec): INPUT: - - ``_parent`` : the parent space of harmonic cocycles. - - ``vec`` : a list of elements in the coefficient module. + - ``_parent`` -- the parent space of harmonic cocycles. + - ``vec`` -- a list of elements in the coefficient module. EXAMPLES:: diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index e22f7460edc..475b2f9a029 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -4473,7 +4473,7 @@ def quotient_module(self, sub, check=True, **kwds): - ``check`` -- boolean (default: ``True``); whether or not to check that ``sub`` is a submodule - - further named arguments, that are passed to the constructor + - ``kwds`` -- further named arguments, that are passed to the constructor of the quotient space EXAMPLES:: @@ -5412,7 +5412,7 @@ def quotient_abstract(self, sub, check=True, **kwds): - ``check`` -- boolean (default: ``True``); whether or not to check that sub is a submodule - - further named arguments, that are currently ignored. + - ``kwds`` -- further named arguments, that are currently ignored. OUTPUT: diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 555c7510d7a..1965691e61d 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -362,7 +362,7 @@ cdef class CVXOPTBackend(GenericBackend): INPUT: - - ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` + - ``coefficients`` -- an iterable with ``(c,v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (real value). diff --git a/src/sage/numerical/backends/generic_sdp_backend.pyx b/src/sage/numerical/backends/generic_sdp_backend.pyx index e1ad583f41c..fa502ef8562 100644 --- a/src/sage/numerical/backends/generic_sdp_backend.pyx +++ b/src/sage/numerical/backends/generic_sdp_backend.pyx @@ -206,7 +206,7 @@ cdef class GenericSDPBackend: INPUT: - - ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` + - ``coefficients`` -- an iterable with ``(c,v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (real value). diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 4ac65e15f40..4ce17a0b409 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -566,7 +566,7 @@ cdef class GLPKBackend(GenericBackend): INPUT: - - ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` + - ``coefficients`` -- an iterable with ``(c,v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (real value). diff --git a/src/sage/numerical/backends/matrix_sdp_backend.pyx b/src/sage/numerical/backends/matrix_sdp_backend.pyx index 5f6ada4bb80..14911719e38 100644 --- a/src/sage/numerical/backends/matrix_sdp_backend.pyx +++ b/src/sage/numerical/backends/matrix_sdp_backend.pyx @@ -250,7 +250,7 @@ cdef class MatrixSDPBackend(GenericSDPBackend): INPUT: - - ``coefficients`` an iterable with ``(c,v)`` pairs where ``c`` + - ``coefficients`` -- an iterable with ``(c,v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (matrix). The pairs come sorted by indices. If c is -1 it represents the constant coefficient. diff --git a/src/sage/numerical/backends/scip_backend.pyx b/src/sage/numerical/backends/scip_backend.pyx index 4cac8e171e0..4f6fd003a43 100644 --- a/src/sage/numerical/backends/scip_backend.pyx +++ b/src/sage/numerical/backends/scip_backend.pyx @@ -426,7 +426,7 @@ cdef class SCIPBackend(GenericBackend): INPUT: - - ``coefficients`` an iterable with ``(c, v)`` pairs where ``c`` + - ``coefficients`` -- an iterable with ``(c, v)`` pairs where ``c`` is a variable index (integer) and ``v`` is a value (real value). diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index 7a217400dcb..951dc405431 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -308,7 +308,7 @@ def parallel(p_iter='fork', ncpus=None, **kwds): - ``ncpus`` -- integer; maximal number of subprocesses to use at the same time - ``timeout`` -- number of seconds until each subprocess is killed (only supported by ``'fork'``; zero means not at all) - - ``reseed_rng``: reseed the rng (random number generator) in each subprocess + - ``reseed_rng`` -- reseed the rng (random number generator) in each subprocess .. warning:: diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 09fb3ffd3db..7c1e33eb269 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -642,7 +642,7 @@ def divisor_of_function(self, r): INPUT: - - ``r`` is a rational function on X + - ``r`` -- a rational function on X OUTPUT: list; the divisor of r represented as a list of coefficients and points. (TODO: This will change to a more structural output in the diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index 464d6de48bd..0715605f722 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -769,7 +769,7 @@ def move_away_from(self, cone): INPUT: - - A ``cone`` of the fan of the toric variety. + - ``cone`` -- a cone of the fan of the toric variety. OUTPUT: diff --git a/src/sage/sets/cartesian_product.py b/src/sage/sets/cartesian_product.py index e29a4fb1a2f..417b30885b5 100644 --- a/src/sage/sets/cartesian_product.py +++ b/src/sage/sets/cartesian_product.py @@ -232,6 +232,7 @@ def _cartesian_product_of_elements(self, elements): Return the Cartesian product of the given ``elements``. This implements :meth:`Sets.CartesianProducts.ParentMethods._cartesian_product_of_elements`. + INPUT: - ``elements`` -- an iterable (e.g. tuple, list) with one element of diff --git a/src/sage/stats/hmm/distributions.pyx b/src/sage/stats/hmm/distributions.pyx index 70fa220d122..75ff33e8cf5 100644 --- a/src/sage/stats/hmm/distributions.pyx +++ b/src/sage/stats/hmm/distributions.pyx @@ -120,7 +120,7 @@ cdef class Distribution: INPUT: - - ``args`` and ``kwds``, passed to the Sage :func:`plot` function + - ``args``, ``kwds`` -- passed to the Sage :func:`plot` function OUTPUT: a :class:`Graphics` object diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index 259398761bc..4747d4e0414 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -204,23 +204,23 @@ def print_options(self, **kwds): INPUT: - All of the input is optional; if present, it should be - in the form of keyword pairs, such as - ``latex_bracket='('``. The allowable keywords are: - - - ``prefix`` - - ``latex_prefix`` - - ``names`` - - ``latex_names`` - - ``bracket`` - - ``latex_bracket`` - - ``scalar_mult`` - - ``latex_scalar_mult`` - - ``tensor_symbol`` - - ``string_quotes`` - - ``sorting_key`` - - ``sorting_reverse`` - - ``iterate_key`` + - ``kwds`` -- All of the input is optional; if present, it should be + in the form of keyword pairs, such as + ``latex_bracket='('``. The allowable keywords are: + + - ``prefix`` + - ``latex_prefix`` + - ``names`` + - ``latex_names`` + - ``bracket`` + - ``latex_bracket`` + - ``scalar_mult`` + - ``latex_scalar_mult`` + - ``tensor_symbol`` + - ``string_quotes`` + - ``sorting_key`` + - ``sorting_reverse`` + - ``iterate_key`` See the documentation for :class:`IndexedGenerators` for descriptions of the effects of setting each of these options. diff --git a/src/sage/topology/cell_complex.py b/src/sage/topology/cell_complex.py index e2276fb8839..628d623f64f 100644 --- a/src/sage/topology/cell_complex.py +++ b/src/sage/topology/cell_complex.py @@ -616,12 +616,23 @@ def cohomology(self, dim=None, base_ring=ZZ, subcomplex=None, INPUT: - - ``dim`` - - ``base_ring`` - - ``subcomplex`` - - ``algorithm`` - - ``verbose`` - - ``reduced`` + - ``dim`` -- integer or list of integers or ``None`` (default: + ``None``); if ``None``, then return the cohomology in every + dimension. If ``dim`` is an integer or list, return the + cohomology in the given dimensions. (Actually, if ``dim`` is + a list, return the cohomology in the range from ``min(dim)`` + to ``max(dim)``.) + - ``base_ring`` -- commutative ring (default: ``ZZ``); must be `\ZZ` or + a field + - ``subcomplex`` -- (default: empty) a subcomplex of this simplicial + complex. Compute the cohomology relative to this subcomplex. + - ``algorithm`` -- string (default: ``'pari'``); the algorithm options + are 'auto', 'dhsw', or 'pari'. See below for a description of what + they mean. + - ``verbose`` -- boolean (default: ``False``); if True, print some + messages as the cohomology is computed + - ``reduced`` -- boolean (default: ``True``); if ``True``, return the + reduced cohomology EXAMPLES:: From b0916fb9b28ab67e7d4467c37bd0a63cad046435 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 22:56:41 +0800 Subject: [PATCH 06/11] Improve name sanitization and disable raising an exception if a field cannot be parsed (for now) --- src/sage_docbuild/ext/sage_transformer.py | 40 ++++++++++++++++--- .../ext/sage_transformer_test.py | 30 +++++++------- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/sage_docbuild/ext/sage_transformer.py b/src/sage_docbuild/ext/sage_transformer.py index 9bba77b3dd3..5d890ebe706 100644 --- a/src/sage_docbuild/ext/sage_transformer.py +++ b/src/sage_docbuild/ext/sage_transformer.py @@ -148,14 +148,42 @@ def _consume_field(self) -> tuple[str, list[str]]: line = self._lines.next() match = re.match(_field_regex, line) if match: - _name = self._escape_args_and_kwargs(match.group("name").strip()) + _name = match.group("name").strip() _desc = match.group("rest").strip() indent = self._get_indent(line) + 1 _descs = [_desc, *self._dedent(self._consume_indented_block(indent))] _descs = self.__class__(_descs).transform() return _name, _descs else: - raise ValueError(f"Invalid field line: {line}") + return "", [] + # TODO: In the future, throw an error in case of errors: + # for exception in ( + # "Input is similar to ``desolve`` command", + # "See IPython documentation.", + # "See the Jupyter documentation.", + # "See :class:`SchemeHomset_generic`.", + # "One of the following:", + # "The :func:`matrix` command takes", + # "The first two arguments specify the base ring", + # "The constructor may be called in any of the following ways", + # "Description of the set:", + # "There are four input formats", + # "There are several ways to construct an", + # "- the inputs are the same as for", + # "There are two ways to call this." + # "The following keywords are used in most cases", + # "The input can be either", + # "Valid keywords are:", + # "Two potential inputs are accepted", + # "Something that defines an affine space", + # "Can be one of the following", + # "The following forms are all accepted", + # "Three possibilities are offered", + # ): + # if exception in line: + # # Don't fail + # return "", [] + # raise ValueError(f"Invalid field line: {line}") def _consume_fields(self, multiple: bool = False) -> list[tuple[str, list[str]]]: """ @@ -168,11 +196,11 @@ def _consume_fields(self, multiple: bool = False) -> list[tuple[str, list[str]]] _name, _desc = self._consume_field() if multiple and _name: fields.extend( - (name.strip().strip(r"`").strip(), _desc) + (self._sanitize_name(name), _desc) for name in _name.split(",") ) elif _name or _desc: - fields.append((_name.strip().strip(r"`").strip(), _desc)) + fields.append((self._sanitize_name(_name), _desc)) return fields def _consume_inline_attribute(self) -> tuple[str, list[str]]: @@ -216,7 +244,9 @@ def _dedent(self, lines: list[str], full: bool = False) -> list[str]: min_indent = self._get_min_indent(lines) return [line[min_indent:] for line in lines] - def _escape_args_and_kwargs(self, name: str) -> str: + def _sanitize_name(self, name: str) -> str: + name = name.strip().strip(r"`").strip() + # Escape args and kwargs so that they are not rendered as emphasized string if name[:2] == "**": return r"\*\*" + name[2:] elif name[:1] == "*": diff --git a/src/sage_docbuild/ext/sage_transformer_test.py b/src/sage_docbuild/ext/sage_transformer_test.py index ec857366aae..f6472dd185d 100644 --- a/src/sage_docbuild/ext/sage_transformer_test.py +++ b/src/sage_docbuild/ext/sage_transformer_test.py @@ -32,25 +32,27 @@ def test_consume_field_with_blank_lines_in_description(): assert descs == ["first line", "", "second line"] -def test_consume_field_escaping_stars(): - lines = ["- *args -- star args"] +def test_consume_fields_escaping_stars(): + lines = ["- ``*args`` -- star args"] dt = DoctestTransformer(lines) - name, descs = dt._consume_field() - assert name == r"\*args" - assert descs == ["star args"] + fields = dt._consume_fields() + assert fields == [ + ("\*args", ["star args"]), + ] - lines = ["- **kwargs -- kw args"] + lines = ["- ``**kwargs`` -- kw args"] dt = DoctestTransformer(lines) - name, descs = dt._consume_field() - assert name == r"\*\*kwargs" - assert descs == ["kw args"] + fields = dt._consume_fields() + assert fields == [ + ("\*\*kwargs", ["kw args"]), + ] -def test_consume_field_invalid_line_raises(): - lines = ["param -- missing leading dash"] - dt = DoctestTransformer(lines) - with pytest.raises(ValueError): - dt._consume_field() +# def test_consume_field_invalid_line_raises(): +# lines = ["param -- missing leading dash"] +# dt = DoctestTransformer(lines) +# with pytest.raises(ValueError): +# dt._consume_field() def test_consume_fields_simple(): From 51bc72ec4d95152e2cdde9de19aa615f5eef14df Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 19 Aug 2025 22:58:16 +0800 Subject: [PATCH 07/11] Temporarily disable raising warnings as errors --- src/sage_docbuild/sphinxbuild.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index 62b2d3cb112..c7e2f18356c 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -22,8 +22,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** import os -import sys import re +import sys + import sphinx import sphinx.cmd.build @@ -321,8 +322,8 @@ def runsphinx(): # We don't use subprocess here, as we don't want to re-initialize Sage # for every docbuild as this takes a while. sphinx.cmd.build.main(sys.argv[1:]) - sys.stderr.raise_errors() - sys.stdout.raise_errors() + #sys.stderr.raise_errors() + #sys.stdout.raise_errors() finally: sys.stdout = saved_stdout sys.stderr = saved_stderr From 03ee44e136aba396d18017826e33cd16671602c6 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 21 Aug 2025 21:23:27 +0800 Subject: [PATCH 08/11] Fix more docstring issues --- src/sage/combinat/finite_state_machine.py | 12 ++++++------ src/sage/geometry/polyhedral_complex.py | 2 +- src/sage/graphs/generic_graph.py | 4 ++-- src/sage/interfaces/kenzo.py | 8 ++++---- src/sage/matrix/matrix_space.py | 4 ++-- src/sage/misc/lazy_string.pyx | 4 ++-- src/sage/numerical/mip.pyx | 2 +- src/sage/plot/plot3d/plot_field3d.py | 4 ++-- src/sage/rings/asymptotic/term_monoid.py | 2 +- src/sage/rings/finite_rings/finite_field_base.pyx | 4 ++-- src/sage/rings/polynomial/multi_polynomial_ideal.py | 2 +- .../polynomial/multi_polynomial_libsingular.pyx | 2 +- .../rings/polynomial/q_integer_valued_polynomials.py | 2 +- src/sage/schemes/toric/fano_variety.py | 8 ++++---- src/sage/sets/condition_set.py | 2 +- src/sage/sets/real_set.py | 2 +- 16 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index c52093b1f94..2e5ebe1e2d5 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -2566,10 +2566,10 @@ class FiniteStateMachine(SageObject): #. an other instance of a finite state machine. - - ``initial_states`` and ``final_states`` -- the initial and + - ``initial_states``, ``final_states`` -- the initial and final states of this machine - - ``input_alphabet`` and ``output_alphabet`` -- the input and + - ``input_alphabet``, ``output_alphabet`` -- the input and output alphabets of this machine - ``determine_alphabets`` -- if ``True``, then the function @@ -5681,7 +5681,7 @@ def process(self, *args, **kwargs): list or tuple of tracks, each of which can be a list or an iterable with entries from the input alphabet. - - ``initial_state`` or ``initial_states`` -- the initial + - ``initial_state``, ``initial_states`` -- the initial state(s) in which the machine starts. Either specify a single one with ``initial_state`` or a list of them with ``initial_states``. If both are given, ``initial_state`` @@ -11290,7 +11290,7 @@ def process(self, *args, **kwargs): list or tuple of tracks, each of which can be a list or an iterable with entries from the input alphabet. - - ``initial_state`` or ``initial_states`` -- the initial + - ``initial_state``, ``initial_states`` -- the initial state(s) in which the machine starts. Either specify a single one with ``initial_state`` or a list of them with ``initial_states``. If both are given, ``initial_state`` @@ -12373,7 +12373,7 @@ def process(self, *args, **kwargs): list or tuple of tracks, each of which can be a list or an iterable with entries from the input alphabet. - - ``initial_state`` or ``initial_states`` -- the initial + - ``initial_state``, ``initial_states`` -- the initial state(s) in which the machine starts. Either specify a single one with ``initial_state`` or a list of them with ``initial_states``. If both are given, ``initial_state`` @@ -13583,7 +13583,7 @@ class FSMProcessIterator(SageObject, Iterator): list or tuple of tracks, each of which can be a list or an iterable with entries from the input alphabet. - - ``initial_state`` or ``initial_states`` -- the initial + - ``initial_state``, ``initial_states`` -- the initial state(s) in which the machine starts. Either specify a single one with ``initial_state`` or a list of them with ``initial_states``. If both are given, ``initial_state`` diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index 7fc0d027e8f..2466782cd63 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -167,7 +167,7 @@ class PolyhedralComplex(GenericCellComplex): if ``True``, then the constructor checks whether the cells are face-to-face, and it raises a :exc:`ValueError` if they are not - - ``is_mutable`` and ``is_immutable`` -- boolean (default: ``True`` and + - ``is_mutable``, ``is_immutable`` -- boolean (default: ``True`` and ``False`` respectively); set ``is_mutable=False`` or ``is_immutable=True`` to make this polyhedral complex immutable diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 0d156608534..4ab795bf899 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -11873,7 +11873,7 @@ def random_vertex_iterator(self, *args, **kwds): INPUT: - - ``*args`` and ``**kwds`` -- arguments to be passed down to the + - ``*args``, ``**kwds`` -- arguments to be passed down to the :meth:`vertex_iterator` method EXAMPLES: @@ -11954,7 +11954,7 @@ def random_edge_iterator(self, *args, **kwds): INPUT: - - ``*args`` and ``**kwds`` -- arguments to be passed down to the + - ``*args``, ``**kwds`` -- arguments to be passed down to the :meth:`edge_iterator` method EXAMPLES: diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index 9e39f9a749b..5ddf35c7d8e 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -372,13 +372,13 @@ def table(self, p, i1, i2, j1, j2): - ``p`` -- the page to print - -- ``i1`` -- the first column to print + - ``i1`` -- the first column to print - -- ``i2`` -- the last column to print + - ``i2`` -- the last column to print - -- ``j1`` -- the first row to print + - ``j1`` -- the first row to print - -- ``j2`` -- the last row to print + - ``j2`` -- the last row to print EXAMPLES:: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index eafab99b0e9..f856f18f1de 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -446,10 +446,10 @@ class MatrixSpace(UniqueRepresentation, Parent): - ``base_ring`` -- a ring - - ``nrows`` or ``row_keys`` -- nonnegative integer; the number of rows, or + - ``nrows``, ``row_keys`` -- nonnegative integer; the number of rows, or a finite family of arbitrary objects that index the rows of the matrix - - ``ncols`` or ``column_keys`` -- nonnegative integer (default: ``nrows``); + - ``ncols``, ``column_keys`` -- nonnegative integer (default: ``nrows``); the number of columns, or a finite family of arbitrary objects that index the columns of the matrix diff --git a/src/sage/misc/lazy_string.pyx b/src/sage/misc/lazy_string.pyx index 553664d639f..4ace585b855 100644 --- a/src/sage/misc/lazy_string.pyx +++ b/src/sage/misc/lazy_string.pyx @@ -84,9 +84,9 @@ def lazy_string(f, *args, **kwargs): INPUT: - ``f`` -- either a callable or a (format) string - - positional arguments that are given to ``f``, either by calling or by + - ``*args`` -- positional arguments that are given to ``f``, either by calling or by applying it as a format string - - named arguments that are forwarded to ``f`` if it is not a string + - ``**kwargs`` -- named arguments that are forwarded to ``f`` if it is not a string EXAMPLES:: diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index f90cc912d97..ff9444d7538 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -316,7 +316,7 @@ cdef class MixedIntegerLinearProgram(SageObject): see :func:`sage.numerical.backends.generic_backend.get_solver` for examples. - - ``maximization`` + - ``maximization`` -- whether to maximize or minimize the objective function. - When set to ``True`` (default), the ``MixedIntegerLinearProgram`` is defined as a maximization. diff --git a/src/sage/plot/plot3d/plot_field3d.py b/src/sage/plot/plot3d/plot_field3d.py index a4f415935d2..f30e79d57da 100644 --- a/src/sage/plot/plot3d/plot_field3d.py +++ b/src/sage/plot/plot3d/plot_field3d.py @@ -33,7 +33,7 @@ def plot_vector_field3d(functions, xrange, yrange, zrange, - ``functions`` -- list of three functions, representing the x-, y-, and z-coordinates of a vector - - ``xrange``, ``yrange``, and ``zrange`` -- three tuples of the + - ``xrange``, ``yrange``, ``zrange`` -- three tuples of the form (var, start, stop), giving the variables and ranges for each axis - ``plot_points`` -- (default: 5) either a number or list of three @@ -48,7 +48,7 @@ def plot_vector_field3d(functions, xrange, yrange, zrange, centered on the points; otherwise, draw the arrows with the tail at the point - - any other keywords are passed on to the :func:`plot` command for each arrow + - ``**kwds`` -- passed on to the :func:`plot` command for each arrow EXAMPLES: diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 588580c3e13..85ea95e6eba 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2114,7 +2114,7 @@ def _create_element_in_extension_(self, growth, coefficient): INPUT: - - ``growth`` and ``coefficient`` -- the element data + - ``growth``, ``coefficient`` -- the element data OUTPUT: an element diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index f205edaffb1..e56077e61b9 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1341,10 +1341,10 @@ cdef class FiniteField(Field): - ``modulus`` -- a polynomial with coefficients in ``self``, or an integer - - ``name`` or ``names`` -- string; the name of the generator + - ``name``, ``names`` -- string; the name of the generator in the new extension - - ``latex_name`` or ``latex_names`` -- string; latex name of + - ``latex_name``, ``latex_names`` -- string; latex name of the generator in the new extension - ``map`` -- boolean (default: ``False``); if ``False``, diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 51958af5290..d628b4c1228 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -5319,7 +5319,7 @@ def random_element(self, degree, compute_gb=False, *args, **kwds): and `f_i` are the elements in the Gröbner basis. Otherwise whatever basis is returned by ``self.gens()`` is used. - - ``*args`` and ``**kwds`` are passed to ``R.random_element()`` with + - ``*args``, ``**kwds`` -- passed to ``R.random_element()`` with ``R = self.ring()``. EXAMPLES: diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index eaf179e0b13..323e6fcf7db 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4896,7 +4896,7 @@ cdef class MPolynomial_libsingular(MPolynomial_libsingular_base): INPUT: - ``right`` -- polynomial - - ``algorithm`` + - ``algorithm`` -- one of the following - ``'ezgcd'`` -- EZGCD algorithm - ``'modular'`` -- multi-modular algorithm (default) - ``**kwds`` -- ignored diff --git a/src/sage/rings/polynomial/q_integer_valued_polynomials.py b/src/sage/rings/polynomial/q_integer_valued_polynomials.py index e989dfff389..8053c63fdbc 100644 --- a/src/sage/rings/polynomial/q_integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/q_integer_valued_polynomials.py @@ -79,7 +79,7 @@ def q_binomial_x(m, n, q=None): INPUT: - - ``m`` and ``n`` -- positive integers + - ``m``, ``n`` -- positive integers - ``q`` -- optional variable diff --git a/src/sage/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index c4823e6680f..c9f16f77525 100644 --- a/src/sage/schemes/toric/fano_variety.py +++ b/src/sage/schemes/toric/fano_variety.py @@ -1264,8 +1264,8 @@ class AnticanonicalHypersurface(AlgebraicScheme_subscheme_toric): - ``P_Delta`` -- :class:`CPR-Fano toric variety ` associated to a reflexive polytope `\Delta` - - see :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for - documentation on all other acceptable parameters + see :meth:`CPRFanoToricVariety_field.anticanonical_hypersurface` for + documentation on all other acceptable parameters OUTPUT: @@ -1377,8 +1377,8 @@ class NefCompleteIntersection(AlgebraicScheme_subscheme_toric): - ``P_Delta`` -- a :class:`CPR-Fano toric variety ` associated to a reflexive polytope `\Delta` - - see :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for - documentation on all other acceptable parameters + see :meth:`CPRFanoToricVariety_field.nef_complete_intersection` for + documentation on all other acceptable parameters OUTPUT: diff --git a/src/sage/sets/condition_set.py b/src/sage/sets/condition_set.py index a0d618ebdde..638d2beb242 100644 --- a/src/sage/sets/condition_set.py +++ b/src/sage/sets/condition_set.py @@ -34,7 +34,7 @@ class ConditionSet(Set_generic, Set_base, Set_boolean_operators, Set_add_sub_ope - ``*predicates`` -- callables - - ``vars`` or ``names`` -- (default: inferred from ``predicates`` if any predicate is + - ``vars``, ``names`` -- (default: inferred from ``predicates`` if any predicate is an element of a :class:`~sage.symbolic.callable.CallableSymbolicExpressionRing_class`) variables or names of variables diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a8ead177946..fc722ed5424 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -913,7 +913,7 @@ class RealSet(UniqueRepresentation, Parent, Set_base, - ``ambient`` -- (default: ``None``) an instance of :class:`~sage.manifolds.differentiable.examples.real_line.RealLine`; construct a subset of it. Using this keyword implies ``structure='differentiable'``. - - ``names`` or ``coordinate`` -- coordinate symbol for the canonical chart; see + - ``names``, ``coordinate`` -- coordinate symbol for the canonical chart; see :class:`~sage.manifolds.differentiable.examples.real_line.RealLine`. Using these keywords implies ``structure='differentiable'``. - ``name``, ``latex_name``, ``start_index`` -- see From f16b616022c99fc11df4f864dec5e38efe4ad237 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 21 Aug 2025 21:28:50 +0800 Subject: [PATCH 09/11] Improve docstring parsing --- src/sage/repl/ipython_tests.py | 6 +- src/sage_docbuild/ext/sage_transformer.py | 59 ++++--- .../ext/sage_transformer_test.py | 148 +++++++++++++++--- src/sage_docbuild/sphinxbuild.py | 4 +- 4 files changed, 172 insertions(+), 45 deletions(-) diff --git a/src/sage/repl/ipython_tests.py b/src/sage/repl/ipython_tests.py index e684012b488..49a4d0c0435 100644 --- a/src/sage/repl/ipython_tests.py +++ b/src/sage/repl/ipython_tests.py @@ -100,7 +100,7 @@ def dummy(argument, optional=None): - ``optional`` -- anything (optional); dummy optional - EXAMPLES:: + EXAMPLES... ... """ @@ -119,7 +119,7 @@ def dummy(argument, optional=None): """ Example class wrapping an STL vector. - EXAMPLES:: + EXAMPLES... ... """ @@ -131,7 +131,7 @@ def __cinit__(self): """ The Cython constructor. - EXAMPLES:: + EXAMPLES... ... File: .../sage/tests/stl_vector.pyx diff --git a/src/sage_docbuild/ext/sage_transformer.py b/src/sage_docbuild/ext/sage_transformer.py index 5d890ebe706..c8f26e0c881 100644 --- a/src/sage_docbuild/ext/sage_transformer.py +++ b/src/sage_docbuild/ext/sage_transformer.py @@ -21,7 +21,7 @@ # "
: " _section_regex = re.compile(r"^\s*([A-Z]+):+\s*$") # - -- -_field_regex = r"^\s*-\s*(?P.+)\s*--\s*(?P.*)$" +_field_regex = r"^\s*-\s*(?P.+?)\s*--\s*(?P.*)$" _single_colon_regex = re.compile(r"(? str | None: @@ -185,19 +188,20 @@ def _consume_field(self) -> tuple[str, list[str]]: # return "", [] # raise ValueError(f"Invalid field line: {line}") - def _consume_fields(self, multiple: bool = False) -> list[tuple[str, list[str]]]: + def _consume_fields( + self, multiple: bool = False, content_has_to_start_with_hyphen: bool = False + ) -> list[tuple[str, list[str]]]: """ Consume all fields/parameters from the docstring, until a section break is encountered. """ self._consume_empty() fields: list[tuple[str, list[str]]] = [] - while not self._is_section_break(): + while not self._is_section_break(content_has_to_start_with_hyphen): _name, _desc = self._consume_field() if multiple and _name: fields.extend( - (self._sanitize_name(name), _desc) - for name in _name.split(",") + (self._sanitize_name(name), _desc) for name in _name.split(",") ) elif _name or _desc: fields.append((self._sanitize_name(_name), _desc)) @@ -305,7 +309,6 @@ def _format_docutils_params( ) -> list[str]: lines = [] for _name, _desc in fields: - _desc = self._strip_empty(_desc) if any(_desc): _desc = self._fix_field_desc(_desc) field = f":{field_role} {_name}: " @@ -387,9 +390,15 @@ def _get_min_indent(self, lines: list[str]) -> int: def _indent(self, lines: list[str], n: int = 4) -> list[str]: return [(" " * n) + line for line in lines] - def _is_indented(self, line: str, indent: int = 1) -> bool: + def _is_indented( + self, line: str, indent: int = 1, content_has_to_start_with_hyphen: bool = False + ) -> bool: for i, s in enumerate(line): - if i >= indent: + if i > indent: + return True + elif i == indent: + if content_has_to_start_with_hyphen: + return s == "-" return True elif not s.isspace(): return False @@ -420,15 +429,20 @@ def _is_section_header(self) -> bool: return section in self._sections return False - def _is_section_break(self) -> bool: + def _is_section_break(self, content_has_to_start_with_hyphen: bool = False) -> bool: line = self._lines.get(0) return ( + # end of input not self._lines + # another section starts or self._is_section_header() + # line not indented enough or ( self._is_in_section - and line is not self._lines.sentinel - and not self._is_indented(line, self._section_indent) + and line + and not self._is_indented( + line, self._section_indent, content_has_to_start_with_hyphen + ) ) ) @@ -484,25 +498,26 @@ def _parse_custom_generic_section(self, section: str) -> list[str]: def _parse_custom_params_style_section(self, section: str) -> list[str]: return self._format_fields(section, self._consume_fields()) - def _parse_custom_returns_style_section(self, section: str) -> list[str]: - fields = self._consume_returns_section() - return self._format_fields(section, fields) - def _parse_generic_section(self, section: str, use_admonition: bool) -> list[str]: lines = self._strip_empty(self._consume_to_next_section()) - lines = self._dedent(lines) if use_admonition: - header = f".. admonition:: {section}" + header = [f".. admonition:: {section}"] lines = self._indent(lines, 3) else: - header = f".. rubric:: {section}" + header = [f".. rubric:: {section}"] + if section.lower() == "examples": + header.append(".. code-block::") + else: + lines = self._dedent(lines) if lines: - return [header, "", *lines, ""] + return [*header, "", *lines, ""] else: - return [header, ""] + return [*header, ""] def _parse_parameters_section(self, section: str) -> list[str]: - fields = self._consume_fields(multiple=True) + fields = self._consume_fields( + multiple=True, content_has_to_start_with_hyphen=True + ) return self._format_docutils_params(fields) def _parse_returns_section(self, section: str) -> list[str]: diff --git a/src/sage_docbuild/ext/sage_transformer_test.py b/src/sage_docbuild/ext/sage_transformer_test.py index f6472dd185d..9d332fbe4e9 100644 --- a/src/sage_docbuild/ext/sage_transformer_test.py +++ b/src/sage_docbuild/ext/sage_transformer_test.py @@ -1,5 +1,3 @@ -import pytest - from .sage_transformer import DoctestTransformer @@ -11,6 +9,30 @@ def test_consume_field_simple(): assert descs == ["description"] +def test_consume_field_encoded_param(): + lines = ["- ``param`` -- description"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == "``param``" + assert descs == ["description"] + + +def test_consume_field_multiple_dashes(): + lines = ["- param -- description with -- multiple dashes"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == "param" + assert descs == ["description with -- multiple dashes"] + + +def test_consume_field_multiple_quotes(): + lines = ["- ``param`` -- description with ``inline code``"] + dt = DoctestTransformer(lines) + name, descs = dt._consume_field() + assert name == "``param``" + assert descs == ["description with ``inline code``"] + + def test_consume_field_multiple_params(): lines = ["- param1, param2 -- description"] dt = DoctestTransformer(lines) @@ -76,8 +98,6 @@ def test_consume_fields_multiple_flag_splits_names(): ("x", ["coordinate values"]), ("y", ["coordinate values"]), ] - # Both entries share the same description list object - assert fields[0][1] is fields[1][1] def test_consume_fields_multiple_flag_false_no_split(): @@ -110,6 +130,55 @@ def test_consume_fields_skips_leading_blank_lines(): assert fields == [("param", ["value"])] +def test_consume_fields_preserves_embedded_lists(): + lines = [ + "- param1 -- value1", + "", + " - item1", + " - item2", + "", + "- param2 -- value2", + ] + dt = DoctestTransformer(lines) + fields = dt._consume_fields() + assert fields == [ + ("param1", ["value1", "", "- item1", "- item2", ""]), + ("param2", ["value2"]), + ] + + +def test_transform_handles_params_with_embedded_lists(): + lines = [ + "INPUT:", + "", + "- param1 -- value1", + "", + " - item1", + " - item2", + "", + "- param2 -- value2", + ] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == [ + ":param param1: value1", + "", + " - item1", + " - item2", + "", + ":param param2: value2", + "", + ] + + +def test_transform_input_section_followed_by_text_doesnot_convert_params(): + # TODO: We probably want to raise an exception instead in the future + lines = ["INPUT:", "some text", "- param1 -- value1", ""] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == ["", "some text", "- param1 -- value1", ""] + + def test_consume_fields_stops_at_section_header(): lines = [ "- p1 -- first", @@ -124,40 +193,30 @@ def test_consume_fields_stops_at_section_header(): def test_consume_returns_section_basic(): - lines = [ - "result line 1", - "", - "OUTPUT:", - ] + lines = ["result line 1", "", "result line 2"] dt = DoctestTransformer(lines) out = dt._consume_returns_section() - assert out == [("", ["result line 1", ""])] - # Header not consumed - assert dt._lines.get(0) == "OUTPUT:" + assert out == [("", ["result line 1", "", "result line 2"])] def test_consume_returns_section_indented_dedents(): lines = [ " first line", " second deeper", - "OUTPUT:", ] dt = DoctestTransformer(lines) out = dt._consume_returns_section() assert out == [("", ["first line", " second deeper"])] - assert dt._lines.get(0) == "OUTPUT:" def test_consume_returns_section_empty(): lines = [ "", "", - "OUTPUT:", ] dt = DoctestTransformer(lines) out = dt._consume_returns_section() assert out == [] - assert dt._lines.get(0) == "OUTPUT:" def test_consume_returns_section_trailing_blank_lines_preserved(): @@ -165,9 +224,62 @@ def test_consume_returns_section_trailing_blank_lines_preserved(): "desc", "", "", - "OUTPUT:", ] dt = DoctestTransformer(lines) out = dt._consume_returns_section() assert out == [("", ["desc", "", ""])] - assert dt._lines.get(0) == "OUTPUT:" + + +def test_consume_returns_section_closed_by_other_section(): + lines = [ + "result line 1", + "", + "INPUT:", # This should close the OUTPUT section + "input line", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["result line 1", ""])] + # Header not consumed + assert dt._lines.get(0) == "INPUT:" + + +def test_transform_returns_section(): + lines = [ + "OUTPUT:", + "result", + ] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == [":returns: result", ""] + + +def test_transform_returns_section_closed_by_other_section(): + lines = [ + "OUTPUT:", + "result", + "ALGORITHM:", + ] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == [":returns: result", "", ".. rubric:: algorithm", ""] + + +def test_transform_examples_section(): + lines = [ + "EXAMPLES:", + "", + " sage: foo1", + " sage: foo2", + "", + ] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == [ + ".. rubric:: Examples", + ".. code-block::", + "", + " sage: foo1", + " sage: foo2", + "", + ] diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index c7e2f18356c..563e251ae4c 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -322,8 +322,8 @@ def runsphinx(): # We don't use subprocess here, as we don't want to re-initialize Sage # for every docbuild as this takes a while. sphinx.cmd.build.main(sys.argv[1:]) - #sys.stderr.raise_errors() - #sys.stdout.raise_errors() + sys.stderr.raise_errors() + sys.stdout.raise_errors() finally: sys.stdout = saved_stdout sys.stderr = saved_stderr From 00c622308c297057a0061f746ac40e047e84600a Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 23 Aug 2025 18:26:26 +0800 Subject: [PATCH 10/11] Improve handling of examples sections --- src/sage_docbuild/ext/sage_transformer.py | 20 +++++++----- .../ext/sage_transformer_test.py | 31 ++++++++++++++++--- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/sage_docbuild/ext/sage_transformer.py b/src/sage_docbuild/ext/sage_transformer.py index c8f26e0c881..eaa21d40911 100644 --- a/src/sage_docbuild/ext/sage_transformer.py +++ b/src/sage_docbuild/ext/sage_transformer.py @@ -224,9 +224,9 @@ def _consume_returns_section(self) -> list[tuple[str, list[str]]]: transformed = self.__class__(lines).transform() return [("", transformed)] - def _consume_section_header(self) -> str: - section = self._lines.next() - return section.strip().strip(":").lower() + def _consume_section_header(self) -> tuple[str, str]: + section = self._lines.next().strip() + return (section, section.strip(":").lower()) def _consume_to_end(self) -> list[str]: lines = [] @@ -463,10 +463,10 @@ def transform(self) -> list[str]: while self._lines: if self._is_section_header(): try: - section = self._consume_section_header() + section, section_normalized = self._consume_section_header() self._is_in_section = True self._section_indent = self._get_current_indent() - lines = self._sections[section](section) + lines = self._sections[section_normalized](section) finally: self._is_in_section = False self._section_indent = 0 @@ -489,7 +489,8 @@ def _parse_attribute_docstring(self) -> list[str]: return lines def _parse_examples_section(self, section: str) -> list[str]: - return self._parse_generic_section("Examples", False) + add_codeblock = section.endswith("::") + return self._parse_generic_section("Examples", False, add_codeblock) def _parse_custom_generic_section(self, section: str) -> list[str]: # for now, no admonition for simple custom sections @@ -498,14 +499,17 @@ def _parse_custom_generic_section(self, section: str) -> list[str]: def _parse_custom_params_style_section(self, section: str) -> list[str]: return self._format_fields(section, self._consume_fields()) - def _parse_generic_section(self, section: str, use_admonition: bool) -> list[str]: + def _parse_generic_section(self, section: str, use_admonition: bool, add_codeblock: bool = False) -> list[str]: lines = self._strip_empty(self._consume_to_next_section()) + section = section.capitalize() + if not section.endswith(":"): + section += ":" if use_admonition: header = [f".. admonition:: {section}"] lines = self._indent(lines, 3) else: header = [f".. rubric:: {section}"] - if section.lower() == "examples": + if add_codeblock: header.append(".. code-block::") else: lines = self._dedent(lines) diff --git a/src/sage_docbuild/ext/sage_transformer_test.py b/src/sage_docbuild/ext/sage_transformer_test.py index 9d332fbe4e9..5584f95c51d 100644 --- a/src/sage_docbuild/ext/sage_transformer_test.py +++ b/src/sage_docbuild/ext/sage_transformer_test.py @@ -262,12 +262,12 @@ def test_transform_returns_section_closed_by_other_section(): ] dt = DoctestTransformer(lines) result = dt.transform() - assert result == [":returns: result", "", ".. rubric:: algorithm", ""] + assert result == [":returns: result", "", ".. rubric:: Algorithm:", ""] -def test_transform_examples_section(): +def test_transform_examples_section_with_double_colon(): lines = [ - "EXAMPLES:", + "EXAMPLES::", "", " sage: foo1", " sage: foo2", @@ -276,10 +276,33 @@ def test_transform_examples_section(): dt = DoctestTransformer(lines) result = dt.transform() assert result == [ - ".. rubric:: Examples", + ".. rubric:: Examples:", ".. code-block::", "", " sage: foo1", " sage: foo2", "", ] + + +def test_transform_examples_section_with_single_colon(): + lines = [ + "EXAMPLES:", + "", + "Some example code::", + "", + " sage: foo1", + " sage: foo2", + "", + ] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == [ + ".. rubric:: Examples:", + "", + "Some example code::", + "", + " sage: foo1", + " sage: foo2", + "", + ] From bd9b77121d653adc97f828d835721c4633e34543 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 27 Aug 2025 18:33:10 +0800 Subject: [PATCH 11/11] Refine function documentation guidelines for types in INPUT and OUTPUT --- src/doc/en/developer/coding_basics.rst | 29 +++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 9c69e96c765..d9adfac97a3 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -353,9 +353,25 @@ information. You can use the existing functions of Sage as templates. The INPUT block describes all arguments that the function accepts. - 1. The type names should be descriptive, but do not have to represent the - exact Sage/Python types. For example, use "integer" for anything that - behaves like an integer, rather than "int" or "Integer". + 1. Put type information first and foremost in the function signature + (Python / Cython annotations). Treat the signature as the single + authoritative place for ordinary type declarations. The INPUT + block must not restate obvious types already conveyed there. + Instead, use the INPUT block to document the *semantic constraints* + and any *non‑obvious* accepted forms: + + - State mathematical or structural conditions: e.g. "prime integer", + "nonnegative", "an iterable of vertices", "a square matrix", etc. + - Mention coercion behavior, mutability expectations, or protocol + requirements ("must implement ``.degree()``", "anything with a + ``.parent()``"). + - Only include explicit type names if they add information not present + in the annotation (e.g. symbolic vs numeric, sparse vs dense). + + Avoid redundant phrases like "``n`` -- integer" when the signature reads + ``def f(n: int, ...)``; write instead "``n`` -- nonnegative" or + "``n`` -- number of iterations (>= 1)". Legacy docstrings may still show + the old style; new and updated code should follow this convention. 2. Mention the default values of the input arguments when applicable. @@ -402,6 +418,13 @@ information. You can use the existing functions of Sage as templates. to be sorted in the same way as the corresponding factors of the characteristic polynomial. + As for INPUT, do not repeat trivial typing information in the OUTPUT + block. Treat the function's return annotation (or an obviously implied + type) as authoritative. Do NOT write just ``OUTPUT: integer`` or + ``OUTPUT: list`` when the signature already makes this clear (e.g., + ``def rank(self) -> int``). Instead, explain the semantic meaning, + constraints, structure, and any subtleties. + - An **EXAMPLES** block for examples. This is not optional. These examples are used for documentation, but they are also