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/environment-3.11-linux.yml b/environment-3.11-linux.yml index cc33017bd18..30fe525893c 100644 --- a/environment-3.11-linux.yml +++ b/environment-3.11-linux.yml @@ -374,6 +374,7 @@ dependencies: - snowballstemmer=3.0.1=pyhd8ed1ab_0 - soupsieve=2.8=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 diff --git a/pyproject.toml b/pyproject.toml index a29991c332b..865438bec42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -228,6 +228,7 @@ docs = [ "jupyter-sphinx", "python-dateutil", "sphinx", + "sphinx-autodoc-typehints", "sphinx-copybutton", "sphinx-inline-tabs", ] diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 12a95dcba78..855973a038b 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -345,9 +345,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. @@ -394,6 +410,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 diff --git a/src/sage/algebras/fusion_rings/fusion_double.py b/src/sage/algebras/fusion_rings/fusion_double.py index 907089aafd1..bcbf8296b1a 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 9fe86a04c6b..afea0adefce 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 @@ -4056,8 +4056,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 f6d222adbfd..103a1899e63 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -1507,7 +1507,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 @@ -1521,33 +1521,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 1e58ac9231d..337dac10b87 100644 --- a/src/sage/categories/affine_weyl_groups.py +++ b/src/sage/categories/affine_weyl_groups.py @@ -217,7 +217,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 0c07b4d85e2..a189a77bffc 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -635,7 +635,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 1634e574653..c44a65bacdd 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -864,6 +864,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 d761db975dc..b8d21acf836 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -978,7 +978,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 d357c6ac7e7..a959ff9dc37 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 444652d5e8f..7dcf05c763d 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 ee9ec2dd4d2..347be92f1dd 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -812,7 +812,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 @@ -870,7 +870,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 01be1106075..1a387467c43 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 @@ -5678,7 +5678,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`` @@ -9395,9 +9395,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 ` @@ -11286,7 +11287,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`` @@ -12369,7 +12370,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`` @@ -13579,7 +13580,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/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py index 8a4b35111ca..ec66a94b5b9 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 d86643bea21..667a54449c9 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 @@ -412,7 +413,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, @@ -719,7 +720,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`` @@ -847,11 +848,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 ed7f3d15de7..6ae202cf156 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 90b38ae52dc..12213b10a99 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -992,7 +992,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/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/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 55fc369cda4..a55bd625b5d 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 9015353ac5a..036574f453d 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 78e36e9d6c3..7707c47cd06 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/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 632d2f1f7fe..568427a3072 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -11886,7 +11886,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: @@ -11967,7 +11967,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/groups/free_group.py b/src/sage/groups/free_group.py index 66a9d31947b..a2e2a7dcefe 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -939,7 +939,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/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/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 7232f0c4ddb..a0a5011db02 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 @@ -1659,6 +1660,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/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/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/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 f1249bf7098..5dcde297919 100644 --- a/src/sage/misc/function_mangling.pyx +++ b/src/sage/misc/function_mangling.pyx @@ -164,7 +164,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/misc/lazy_string.pyx b/src/sage/misc/lazy_string.pyx index ef19e25af7c..b3834c66b3c 100644 --- a/src/sage/misc/lazy_string.pyx +++ b/src/sage/misc/lazy_string.pyx @@ -83,9 +83,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/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 5d57693fd14..438c7f802d9 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -55,11 +55,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 b3d2a58fd85..3f48610928c 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 545b4f6453b..a22e42c45be 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/numerical/mip.pyx b/src/sage/numerical/mip.pyx index c15e268e89e..989c39e2ee7 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/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/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/repl/ipython_tests.py b/src/sage/repl/ipython_tests.py index d67da8353ae..f1aadef881f 100644 --- a/src/sage/repl/ipython_tests.py +++ b/src/sage/repl/ipython_tests.py @@ -99,7 +99,7 @@ def dummy(argument, optional=None): - ``optional`` -- anything (optional); dummy optional - EXAMPLES:: + EXAMPLES... ... """ @@ -118,7 +118,7 @@ def dummy(argument, optional=None): """ Example class wrapping an STL vector. - EXAMPLES:: + EXAMPLES... ... """ @@ -130,7 +130,7 @@ def __cinit__(self): """ The Cython constructor. - EXAMPLES:: + EXAMPLES... ... File: .../sage/tests/stl_vector.pyx diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 931aeae94dd..a560286e278 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2082,7 +2082,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 d3aadd8d990..29e445eced6 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1350,10 +1350,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 12584501aab..1355ad3e4b4 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -5278,7 +5278,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 2ace29d079f..7fbd1824c4c 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4974,7 +4974,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/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 4c3e644145b..9392b3003c0 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -641,7 +641,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/schemes/toric/fano_variety.py b/src/sage/schemes/toric/fano_variety.py index 355f7d593c1..4a1f5efb05c 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/cartesian_product.py b/src/sage/sets/cartesian_product.py index 62d56f1b324..3f3e3637439 100644 --- a/src/sage/sets/cartesian_product.py +++ b/src/sage/sets/cartesian_product.py @@ -241,6 +241,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/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 7f909c69655..b6b999b2f6f 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -912,7 +912,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 diff --git a/src/sage/stats/hmm/distributions.pyx b/src/sage/stats/hmm/distributions.pyx index 9969018fa40..2839add9f95 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 24f75d956ca..0901ce0efbc 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -203,23 +203,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 f69522b7c33..877391d36fd 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:: diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index b313be44c3e..b3dbf290bf1 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..eaa21d40911 --- /dev/null +++ b/src/sage_docbuild/ext/sage_transformer.py @@ -0,0 +1,609 @@ +"""Classes for docstring parsing and formatting.""" +# Heavily inspired by 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 +from typing import TypeVar + +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*$)" +) + + +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. + """ + + 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 # type: ignore[return-value] + + 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'), + "algorithm": self._parse_custom_generic_section, + "references": self._parse_custom_generic_section, + "authors": self._parse_custom_generic_section, + } + + 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 = 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: + 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, 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(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(",") + ) + elif _name or _desc: + fields.append((self._sanitize_name(_name), _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) -> tuple[str, str]: + section = self._lines.next().strip() + return (section, section.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 _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] == "*": + 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: + 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, content_has_to_start_with_hyphen: bool = False + ) -> bool: + for i, s in enumerate(line): + 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 + 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, 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 + and not self._is_indented( + line, self._section_indent, content_has_to_start_with_hyphen + ) + ) + ) + + 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, section_normalized = self._consume_section_header() + self._is_in_section = True + self._section_indent = self._get_current_indent() + lines = self._sections[section_normalized](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]: + 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 + 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_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 add_codeblock: + header.append(".. code-block::") + else: + lines = self._dedent(lines) + if lines: + return [*header, "", *lines, ""] + else: + return [*header, ""] + + def _parse_parameters_section(self, section: str) -> list[str]: + 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]: + 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..5584f95c51d --- /dev/null +++ b/src/sage_docbuild/ext/sage_transformer_test.py @@ -0,0 +1,308 @@ +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_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) + 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_fields_escaping_stars(): + lines = ["- ``*args`` -- star args"] + dt = DoctestTransformer(lines) + fields = dt._consume_fields() + assert fields == [ + ("\*args", ["star args"]), + ] + + lines = ["- ``**kwargs`` -- kw args"] + dt = DoctestTransformer(lines) + 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_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"]), + ] + + +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_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", + "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", "", "result line 2"] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["result line 1", "", "result line 2"])] + + +def test_consume_returns_section_indented_dedents(): + lines = [ + " first line", + " second deeper", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["first line", " second deeper"])] + + +def test_consume_returns_section_empty(): + lines = [ + "", + "", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [] + + +def test_consume_returns_section_trailing_blank_lines_preserved(): + lines = [ + "desc", + "", + "", + ] + dt = DoctestTransformer(lines) + out = dt._consume_returns_section() + assert out == [("", ["desc", "", ""])] + + +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_with_double_colon(): + lines = [ + "EXAMPLES::", + "", + " sage: foo1", + " sage: foo2", + "", + ] + dt = DoctestTransformer(lines) + result = dt.transform() + assert result == [ + ".. 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", + "", + ] diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index 62b2d3cb112..563e251ae4c 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