diff --git a/build/pkgs/mathjax/SPKG.rst b/build/pkgs/mathjax/SPKG.rst
index 2cb2508b733..de7b4baa3ff 100644
--- a/build/pkgs/mathjax/SPKG.rst
+++ b/build/pkgs/mathjax/SPKG.rst
@@ -6,7 +6,7 @@ Description
MathJax is a JavaScript library for displaying mathematical formulas.
-MathJax is used by the Sage documentation built by Sphinx.
+MathJax is used in the Sage documentation built by Sphinx.
License
-------
@@ -30,3 +30,8 @@ Special Update/Build Instructions
None
+
+Patches
+-------
+
+None.
diff --git a/build/pkgs/mathjax/checksums.ini b/build/pkgs/mathjax/checksums.ini
index 6a6b233b2ae..76e362f6b28 100644
--- a/build/pkgs/mathjax/checksums.ini
+++ b/build/pkgs/mathjax/checksums.ini
@@ -2,3 +2,4 @@ tarball=mathjax-VERSION.tar.gz
sha1=3f7abecf8cacd7f5d7f9ae6c3baca7739101c17d
md5=ba1a65ab58aaad6c84f39735c619bc34
cksum=1142131398
+upstream_url=https://trac.sagemath.org/raw-attachment/ticket/25833/mathjax-3.2.0.tar.gz
diff --git a/build/pkgs/mathjax/spkg-src b/build/pkgs/mathjax/spkg-src
index 49f81e7a993..bd7fa941cc9 100755
--- a/build/pkgs/mathjax/spkg-src
+++ b/build/pkgs/mathjax/spkg-src
@@ -4,11 +4,11 @@ set -e
[ -n "${SAGE_ROOT}" ] || SAGE_ROOT="$(pwd)/../../../"
-# Determine the latest version
+# determine the latest version
GIT_VERSION="$(curl https://github.com/mathjax/MathJax/releases | grep 'MathJax v' | head -1 | sed 's|^.*MathJax v||g' | sed 's/\s*$//g')"
echo "GIT_VERSION=$GIT_VERSION"
-# Fetch and rename the latest version
+# fetch and rename the latest version
URL="https://github.com/mathjax/MathJax/archive/refs/tags/${GIT_VERSION}.zip"
echo "Downloading $URL"
rm -rf src
@@ -18,51 +18,17 @@ else
tar xzf "$UPSTREAM_SOURCE_TARBALL"
fi
-# Put files under mathjax directory
+# put files under mathjax directory
mkdir src
mv MathJax-${GIT_VERSION}/es5 src/mathjax
rm -r MathJax-${GIT_VERSION}
-
-# The following block of commented-out lines were used to reduce the package
-# size of MathJax2. We keep these lines for the future when we will want to
-# reuse and rewrite them to remove unnecessary font files from MathJax3.
-
-## Trimming I -- removing files unnecessary for deployment
-#FILEDIRS_TO_REMOVE='docs/ test/ unpacked/ .gitignore README-branch.txt README.md bower.json'
-#for filedir in ${FILEDIRS_TO_REMOVE} ; do
-# rm -rf "src/${filedir}"
-#done
-#
-## Trimming II -- not strictly necessary (requires the patch nopng_config.patch)
-#rm -rf 'src/fonts/HTML-CSS/TeX/png/'
-#
-## Trimming III -- fonts
-#FONTS_TO_REMOVE='Asana-Math Gyre-Pagella Gyre-Termes Latin-Modern Neo-Euler'
-#for font in ${FONTS_TO_REMOVE} ; do
-# find . -type d -name "${font}" -prune -exec rm -rf {} \;
-#done
-#
-#FONT_FORMATS_TO_REMOVE='eot otf svg'
-#for fontformat in ${FONT_FORMATS_TO_REMOVE} ; do
-# find . -type d -name "${fontformat}" -prune -exec rm -rf {} \;
-#done
-#
-## Trimming IV -- reducing input and output options
-#OUTPUT_OPTIONS_TO_REMOVE='NativeMML SVG'
-#for output in ${OUTPUT_OPTIONS_TO_REMOVE} ; do
-# rm -rf "src/jax/output/${output}"
-#done
-
-
PACKAGE_VERSION=${GIT_VERSION}
-# Repackage.
+# repackage
tar czf "$SAGE_ROOT/upstream/mathjax-${PACKAGE_VERSION}.tar.gz" src
rm -rf src
-# Update package info
+# update package info
echo "${PACKAGE_VERSION}" > 'package-version.txt'
"$SAGE_ROOT"/sage --package fix-checksum mathjax
-
-
diff --git a/pkgs/sagemath-objects/MANIFEST.in b/pkgs/sagemath-objects/MANIFEST.in
index fb7c047d6c6..ee82e1ae64c 100644
--- a/pkgs/sagemath-objects/MANIFEST.in
+++ b/pkgs/sagemath-objects/MANIFEST.in
@@ -62,7 +62,7 @@ include sage/misc/flatten.* # dep of sage/categories/coxeter_groups.
include sage/misc/lazy_import*.*
include sage/misc/sageinspect.* # dep of sage/misc/lazy_import
-graft sage/docs # dep of sage/misc/lazy_import
+include sage/misc/instancedoc.* # dep of sage/misc/lazy_import
include sage/misc/persist.*
include sage/misc/sage_unittest.* # dep of sage/misc/persist
diff --git a/src/doc/ca/intro/conf.py b/src/doc/ca/intro/conf.py
index 9cafb361385..2f4eb7f1873 100644
--- a/src/doc/ca/intro/conf.py
+++ b/src/doc/ca/intro/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/de/a_tour_of_sage/conf.py b/src/doc/de/a_tour_of_sage/conf.py
index a3724e5d1f8..47355ae5a22 100644
--- a/src/doc/de/a_tour_of_sage/conf.py
+++ b/src/doc/de/a_tour_of_sage/conf.py
@@ -12,13 +12,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/de/thematische_anleitungen/conf.py b/src/doc/de/thematische_anleitungen/conf.py
index 82a9ec207b2..114346944d5 100644
--- a/src/doc/de/thematische_anleitungen/conf.py
+++ b/src/doc/de/thematische_anleitungen/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/de/tutorial/conf.py b/src/doc/de/tutorial/conf.py
index 4b7ade4d394..16804d981c9 100644
--- a/src/doc/de/tutorial/conf.py
+++ b/src/doc/de/tutorial/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/a_tour_of_sage/conf.py b/src/doc/en/a_tour_of_sage/conf.py
index 60f8b638750..89225513782 100644
--- a/src/doc/en/a_tour_of_sage/conf.py
+++ b/src/doc/en/a_tour_of_sage/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/constructions/conf.py b/src/doc/en/constructions/conf.py
index d50acf00386..eee2feb033a 100644
--- a/src/doc/en/constructions/conf.py
+++ b/src/doc/en/constructions/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/developer/conf.py b/src/doc/en/developer/conf.py
index 3be07cc7d3e..1ee9e105947 100644
--- a/src/doc/en/developer/conf.py
+++ b/src/doc/en/developer/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/faq/conf.py b/src/doc/en/faq/conf.py
index 8b70e81e4b6..42c3378b129 100644
--- a/src/doc/en/faq/conf.py
+++ b/src/doc/en/faq/conf.py
@@ -12,13 +12,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import *
+from sage_docbuild.conf import release
+from sage_docbuild.conf import *
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/installation/conf.py b/src/doc/en/installation/conf.py
index 268f9fa4648..33ed20fa8e9 100644
--- a/src/doc/en/installation/conf.py
+++ b/src/doc/en/installation/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/prep/conf.py b/src/doc/en/prep/conf.py
index 4891ddc7868..bbc6663b3df 100644
--- a/src/doc/en/prep/conf.py
+++ b/src/doc/en/prep/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/reference/conf.py b/src/doc/en/reference/conf.py
index 6ce70316d9f..86aa0b05a81 100644
--- a/src/doc/en/reference/conf.py
+++ b/src/doc/en/reference/conf.py
@@ -12,13 +12,13 @@
import os
from sage.env import SAGE_DOC_SRC, SAGE_DOC
-from sage.docs.conf import release, latex_elements, exclude_patterns
-from sage.docs.conf import *
+from sage_docbuild.conf import release, latex_elements, exclude_patterns
+from sage_docbuild.conf import *
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/reference/conf_sub.py b/src/doc/en/reference/conf_sub.py
index 9d22a36e497..b6f20311d68 100644
--- a/src/doc/en/reference/conf_sub.py
+++ b/src/doc/en/reference/conf_sub.py
@@ -12,13 +12,13 @@
import os
from sage.env import SAGE_DOC_SRC, SAGE_DOC
-from sage.docs.conf import release, exclude_patterns
-from sage.docs.conf import *
+from sage_docbuild.conf import release, exclude_patterns
+from sage_docbuild.conf import *
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/reference/documentation/conf.py b/src/doc/en/reference/documentation/conf.py
new file mode 120000
index 00000000000..2bdf7e68470
--- /dev/null
+++ b/src/doc/en/reference/documentation/conf.py
@@ -0,0 +1 @@
+../conf_sub.py
\ No newline at end of file
diff --git a/src/doc/en/reference/documentation/index.rst b/src/doc/en/reference/documentation/index.rst
new file mode 100644
index 00000000000..13d17594db3
--- /dev/null
+++ b/src/doc/en/reference/documentation/index.rst
@@ -0,0 +1,12 @@
+Documentation System
+====================
+
+.. toctree::
+ :maxdepth: 1
+
+ sage_docbuild/__main__
+ sage_docbuild/builders
+ sage_docbuild/build_options
+ sage_docbuild/sphinxbuild
+ sage_docbuild/conf
+ sage_docbuild/utils
diff --git a/src/doc/en/reference/footer.txt b/src/doc/en/reference/footer.txt
index 94b024d6dab..22b60ffa26f 100644
--- a/src/doc/en/reference/footer.txt
+++ b/src/doc/en/reference/footer.txt
@@ -1,13 +1,13 @@
Indices and Tables
==================
+* `Index <../genindex.html>`_
+* `Module Index <../py-modindex.html>`_
+* `Search Page <../search.html>`_
+
..
comment: the following math environment forces Sphinx to load MathJax
in the index.rst pages. Do not delete it!
.. math::
:nowrap:
-
-* `Index <../genindex.html>`_
-* `Module Index <../py-modindex.html>`_
-* `Search Page <../search.html>`_
diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst
index 9cb0d0b47e1..065bccac955 100644
--- a/src/doc/en/reference/index.rst
+++ b/src/doc/en/reference/index.rst
@@ -7,7 +7,7 @@ Welcome to the Sage Reference Manual!
Here you find documentation for all of `Sage `_'s
features, illustrated with lots of examples. A thematic index follows.
-This documentation is licensed under the `Creative Commons Attribution-Share Alike 3.0 License`__:math:`.`
+This documentation is licensed under the `Creative Commons Attribution-Share Alike 3.0 License`__.
__ http://creativecommons.org/licenses/by-sa/3.0/
@@ -97,7 +97,7 @@ Geometry, Topology, and Homological Algebra
* :doc:`Euclidean Spaces and Vector Calculus `
* :doc:`Combinatorial and Discrete Geometry `
-* :doc:`Cell Complexes, Simplicial Complexes, and
+* :doc:`Cell Complexes, Simplicial Complexes, and
Simplicial Sets `
* :doc:`Manifolds and Differential Geometry `
* :doc:`Hyperbolic Geometry `
@@ -157,6 +157,11 @@ Interfaces
* :doc:`C/C++ Library Interfaces `
* :doc:`Python Technicalities `
+Documentation System
+--------------------
+
+* :doc:`Documentation System `
+
General Information
===================
@@ -170,3 +175,10 @@ Indices and Tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+
+..
+ comment: the following math environment forces Sphinx to load MathJax
+ in the index.rst pages. Do not delete it!
+
+.. math::
+ :nowrap:
diff --git a/src/doc/en/thematic_tutorials/conf.py b/src/doc/en/thematic_tutorials/conf.py
index f5c5af6cb62..3b638d40173 100644
--- a/src/doc/en/thematic_tutorials/conf.py
+++ b/src/doc/en/thematic_tutorials/conf.py
@@ -12,13 +12,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import *
+from sage_docbuild.conf import release
+from sage_docbuild.conf import *
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py
index 0fba6acc072..cdcbdb584c9 100644
--- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py
+++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/thematic_tutorials/numerical_sage/conf.py b/src/doc/en/thematic_tutorials/numerical_sage/conf.py
index da30268c1b4..08e174fde3b 100644
--- a/src/doc/en/thematic_tutorials/numerical_sage/conf.py
+++ b/src/doc/en/thematic_tutorials/numerical_sage/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/tutorial/conf.py b/src/doc/en/tutorial/conf.py
index d3f2ddf1204..b2b525d2c2a 100644
--- a/src/doc/en/tutorial/conf.py
+++ b/src/doc/en/tutorial/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/website/conf.py b/src/doc/en/website/conf.py
index caf790a3977..7794fb9324a 100644
--- a/src/doc/en/website/conf.py
+++ b/src/doc/en/website/conf.py
@@ -10,13 +10,13 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-from sage.docs.conf import release
-from sage.docs.conf import * # NOQA
+from sage_docbuild.conf import release
+from sage_docbuild.conf import * # NOQA
# Add any paths that contain custom static files (such as style sheets),
# relative to this directory to html_static_path. They are copied after the
# builtin static files, so a file named "default.css" will overwrite the
-# builtin "default.css". html_common_static_path imported from sage.docs.conf
+# builtin "default.css". html_common_static_path imported from sage_docbuild.conf
# contains common paths.
html_static_path = [] + html_common_static_path
diff --git a/src/doc/en/website/templates/index.html b/src/doc/en/website/templates/index.html
index 5f34a99dc62..d8895243052 100644
--- a/src/doc/en/website/templates/index.html
+++ b/src/doc/en/website/templates/index.html
@@ -15,6 +15,13 @@
display: none;
{%- endif %}
}
+ table.contentstable {
+ align: center;
+ border-spacing: 20px;
+ }
+ table.contentstable span { {# trac #33600 comment:22 #}
+ border-spacing: initial;
+ }
{% endblock %}
@@ -33,7 +40,7 @@
',
- html)
-
- # From index.html, we want the preamble and the tail.
- html_end_preamble = html.find(r'') + len(r'')
-
- # For the content, we modify doc/en/reference/index.rst, which
- # has two parts: the body and the table of contents.
- with open(os.path.join(SAGE_DOC_SRC, self.lang, 'reference', 'index.rst')) as f:
- rst = f.read()
- # Get rid of todolist and miscellaneous rst markup.
- rst = rst.replace('.. _reference-manual:\n\n', '')
- rst = re.sub(r'\\\\', r'\\', rst)
- # Replace rst links with html links. There are three forms:
- #
- # `blah`__ followed by __ LINK
- #
- # `blah `_
- #
- # :doc:`blah `
- #
- # Change the first and the second forms to
- #
- # blah
- #
- # Change the third form to
- #
- # blah
- #
- rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)',
- r'\1.', rst)
- rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_',
- r'\1', rst)
- rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`',
- r'\1 ', rst)
- # Body: add paragraph
')
- # TOC: don't include the indices
- start = rst.find('\nUser Interfaces')
- end = rst.find('Indices and Tables')
- rst_toc = rst[start:end]
- # change * to
; change rst headers to html headers
- rst_toc = re.sub(r'\*(.*)\n',
- r'
\n', rst_toc)
- # now write the file.
- with open(os.path.join(output_dir, 'index.html'), 'w') as new_index:
- new_index.write(html[:html_end_preamble])
- new_index.write('
',
+ html)
+
+ # From index.html, we want the preamble and the tail.
+ html_end_preamble = html.find(r'') + len(r'')
+
+ # For the content, we modify doc/en/reference/index.rst, which
+ # has two parts: the body and the table of contents.
+ with open(os.path.join(SAGE_DOC_SRC, self.lang, 'reference', 'index.rst')) as f:
+ rst = f.read()
+ # Get rid of todolist and miscellaneous rst markup.
+ rst = rst.replace('.. _reference-manual:\n\n', '')
+ rst = re.sub(r'\\\\', r'\\', rst)
+ # Replace rst links with html links. There are three forms:
+ #
+ # `blah`__ followed by __ LINK
+ #
+ # `blah `_
+ #
+ # :doc:`blah `
+ #
+ # Change the first and the second forms to
+ #
+ # blah
+ #
+ # Change the third form to
+ #
+ # blah
+ #
+ rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)',
+ r'\1.', rst)
+ rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_',
+ r'\1', rst)
+ rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`',
+ r'\1 ', rst)
+ # Body: add paragraph
')
+ # TOC: don't include the indices
+ start = rst.find('\nUser Interfaces')
+ end = rst.find('Indices and Tables')
+ rst_toc = rst[start:end]
+ # change * to
; change rst headers to html headers
+ rst_toc = re.sub(r'\*(.*)\n',
+ r'
\n', rst_toc)
+ # now write the file.
+ with open(os.path.join(output_dir, 'index.html'), 'w') as new_index:
+ new_index.write(html[:html_end_preamble])
+ new_index.write('
\n\n')
+ new_index.write(html[html_bottom:])
+ logger.warning('''
+PDF documents have been created in subdirectories of
+
+ %s
+
+Alternatively, you can open
+
+ %s
+
+for a webpage listing all of the documents.''' % (output_dir,
+ os.path.join(output_dir,
+ 'index.html')))
+
+
+class ReferenceSubBuilder(DocBuilder):
+ """
+ This class builds sub-components of the reference manual. It is
+ responsible for making sure that the auto generated reST files for the
+ Sage library are up to date.
+
+ When building any output, we must first go through and check
+ to see if we need to update any of the autogenerated reST
+ files. There are two cases where this would happen:
+
+ 1. A new module gets added to one of the toctrees.
+
+ 2. The actual module gets updated and possibly contains a new
+ title.
+ """
+ def __init__(self, *args, **kwds):
+ DocBuilder.__init__(self, *args, **kwds)
+ self._wrap_builder_helpers()
+
+ def _wrap_builder_helpers(self):
+ from functools import partial, update_wrapper
+ for attr in dir(self):
+ if hasattr(getattr(self, attr), 'is_output_format'):
+ f = partial(self._wrapper, attr)
+ f.is_output_format = True
+ update_wrapper(f, getattr(self, attr))
+ setattr(self, attr, f)
+
+ def _wrapper(self, build_type, *args, **kwds):
+ """
+ This is the wrapper around the builder_helper methods that
+ goes through and makes sure things are up to date.
+ """
+ # Force regeneration of all modules if the inherited
+ # and/or underscored members options have changed.
+ cache = self.get_cache()
+ force = False
+ try:
+ if (cache['option_inherited'] != self._options.inherited or
+ cache['option_underscore'] != self._options.underscore):
+ logger.info("Detected change(s) in inherited and/or underscored members option(s).")
+ force = True
+ except KeyError:
+ force = True
+ cache['option_inherited'] = self._options.inherited
+ cache['option_underscore'] = self._options.underscore
+ self.save_cache()
+
+ # After "sage -clone", refresh the reST file mtimes in
+ # environment.pickle.
+ if self._options.update_mtimes:
+ logger.info("Checking for reST file mtimes to update...")
+ self.update_mtimes()
+
+ if force:
+ # Write reST files for all modules from scratch.
+ self.clean_auto()
+ for module_name in self.get_all_included_modules():
+ self.write_auto_rest_file(module_name)
+ else:
+ # Write reST files for new and updated modules.
+ for module_name in self.get_new_and_updated_modules():
+ self.write_auto_rest_file(module_name)
+
+ # Copy over the custom reST files from _sage
+ _sage = os.path.join(self.dir, '_sage')
+ if os.path.exists(_sage):
+ logger.info("Copying over custom reST files from %s ...", _sage)
+ shutil.copytree(_sage, os.path.join(self.dir, 'sage'))
+
+ getattr(DocBuilder, build_type)(self, *args, **kwds)
+
+ def cache_filename(self):
+ """
+ Return the filename where the pickle of the reference cache
+ is stored.
+ """
+ return os.path.join(self._doctrees_dir(), 'reference.pickle')
+
+ @cached_method
+ def get_cache(self):
+ """
+ Retrieve the reference cache which contains the options previously used
+ by the reference builder.
+
+ If it doesn't exist, then we just return an empty dictionary. If it
+ is corrupted, return an empty dictionary.
+ """
+ filename = self.cache_filename()
+ if not os.path.exists(filename):
+ return {}
+ with open(self.cache_filename(), 'rb') as file:
+ try:
+ cache = pickle.load(file)
+ except Exception:
+ logger.debug("Cache file '%s' is corrupted; ignoring it..." % filename)
+ cache = {}
+ else:
+ logger.debug("Loaded the reference cache: %s", filename)
+ return cache
+
+ def save_cache(self):
+ """
+ Pickle the current reference cache for later retrieval.
+ """
+ cache = self.get_cache()
+ try:
+ with open(self.cache_filename(), 'wb') as file:
+ pickle.dump(cache, file)
+ logger.debug("Saved the reference cache: %s", self.cache_filename())
+ except PermissionError:
+ logger.debug("Permission denied for the reference cache: %s", self.cache_filename())
+
+ def get_sphinx_environment(self):
+ """
+ Return the Sphinx environment for this project.
+ """
+ class FakeConfig(object):
+ values = tuple()
+
+ class FakeApp(object):
+ def __init__(self, dir):
+ self.srcdir = dir
+ self.config = FakeConfig()
+
+ env_pickle = os.path.join(self._doctrees_dir(), 'environment.pickle')
+ try:
+ with open(env_pickle, 'rb') as f:
+ env = pickle.load(f)
+ env.app = FakeApp(self.dir)
+ env.config.values = env.app.config.values
+ logger.debug("Opened Sphinx environment: %s", env_pickle)
+ return env
+ except (IOError, EOFError) as err:
+ logger.debug(
+ f"Failed to open Sphinx environment '{env_pickle}'", exc_info=True)
+
+ def update_mtimes(self):
+ """
+ Update the modification times for reST files in the Sphinx
+ environment for this project.
+ """
+ env = self.get_sphinx_environment()
+ if env is not None:
+ for doc in env.all_docs:
+ env.all_docs[doc] = time.time()
+ logger.info("Updated %d reST file mtimes", len(env.all_docs))
+ # This is the only place we need to save (as opposed to
+ # load) Sphinx's pickle, so we do it right here.
+ env_pickle = os.path.join(self._doctrees_dir(),
+ 'environment.pickle')
+
+ # When cloning a new branch (see
+ # SAGE_LOCAL/bin/sage-clone), we hard link the doc output.
+ # To avoid making unlinked, potentially inconsistent
+ # copies of the environment, we *don't* use
+ # env.topickle(env_pickle), which first writes a temporary
+ # file. We adapt sphinx.environment's
+ # BuildEnvironment.topickle:
+
+ # remove unpicklable attributes
+ env.set_warnfunc(None)
+ del env.config.values
+ with open(env_pickle, 'wb') as picklefile:
+ # remove potentially pickling-problematic values from config
+ for key, val in vars(env.config).items():
+ if key.startswith('_') or isinstance(val, (types.ModuleType,
+ types.FunctionType,
+ type)):
+ del env.config[key]
+ pickle.dump(env, picklefile, pickle.HIGHEST_PROTOCOL)
+
+ logger.debug("Saved Sphinx environment: %s", env_pickle)
+
+ def get_modified_modules(self):
+ """
+ Return an iterator for all the modules that have been modified
+ since the documentation was last built.
+ """
+ env = self.get_sphinx_environment()
+ if env is None:
+ logger.debug("Stopped check for modified modules.")
+ return
+ try:
+ added, changed, removed = env.get_outdated_files(False)
+ logger.info("Sphinx found %d modified modules", len(changed))
+ except OSError as err:
+ logger.debug("Sphinx failed to determine modified modules: %s", err)
+ return
+ for name in changed:
+ # Only pay attention to files in a directory sage/... In
+ # particular, don't treat a file like 'sagetex.rst' in
+ # doc/en/reference/misc as an autogenerated file: see
+ # #14199.
+ if name.startswith('sage' + os.sep):
+ yield name
+
+ def print_modified_modules(self):
+ """
+ Print a list of all the modules that have been modified since
+ the documentation was last built.
+ """
+ for module_name in self.get_modified_modules():
+ print(module_name)
+
+ def get_all_rst_files(self, exclude_sage=True):
+ """
+ Return an iterator for all rst files which are not
+ autogenerated.
+ """
+ for directory, subdirs, files in os.walk(self.dir):
+ if exclude_sage and directory.startswith(os.path.join(self.dir, 'sage')):
+ continue
+ for filename in files:
+ if not filename.endswith('.rst'):
+ continue
+ yield os.path.join(directory, filename)
+
+ def get_all_included_modules(self):
+ """
+ Return an iterator for all modules which are included in the
+ reference manual.
+ """
+ for filename in self.get_all_rst_files():
+ for module in self.get_modules(filename):
+ yield module
+
+ def get_new_and_updated_modules(self):
+ """
+ Return an iterator for all new and updated modules that appear in
+ the toctrees, and remove obsolete old modules.
+ """
+ env = self.get_sphinx_environment()
+ if env is None:
+ all_docs = {}
+ else:
+ all_docs = env.all_docs
+
+ new_modules = []
+ updated_modules = []
+ old_modules = []
+ for module_name in self.get_all_included_modules():
+ docname = module_name.replace('.', os.path.sep)
+
+ if docname not in all_docs:
+ new_modules.append(module_name)
+ yield module_name
+ continue
+
+ # get the modification timestamp of the reST doc for the module
+ mtime = all_docs[docname]
+ try:
+ with warnings.catch_warnings():
+ # primarily intended to ignore deprecation warnings
+ warnings.simplefilter("ignore")
+ __import__(module_name)
+ except ImportError as err:
+ logger.error("Warning: Could not import %s %s", module_name, err)
+ raise
+
+ module_filename = sys.modules[module_name].__file__
+ if (module_filename.endswith('.pyc') or module_filename.endswith('.pyo')):
+ source_filename = module_filename[:-1]
+ if (os.path.exists(source_filename)):
+ module_filename = source_filename
+ newtime = os.path.getmtime(module_filename)
+
+ if newtime > mtime:
+ updated_modules.append(module_name)
+ yield module_name
+ else: # keep good old module
+ old_modules.append(module_name)
+
+ removed_modules = []
+ for docname in all_docs.keys():
+ if docname.startswith('sage' + os.path.sep):
+ module_name = docname.replace(os.path.sep, '.')
+ if not (module_name in old_modules or module_name in updated_modules):
+ try:
+ os.remove(os.path.join(self.dir, docname) + '.rst')
+ except OSError: # already removed
+ pass
+ logger.debug("Deleted auto-generated reST file {}".format(docname))
+ removed_modules.append(module_name)
+
+ logger.info("Found %d new modules", len(new_modules))
+ logger.info("Found %d updated modules", len(updated_modules))
+ logger.info("Removed %d obsolete modules", len(removed_modules))
+
+ def print_new_and_updated_modules(self):
+ """
+ Print all the modules that appear in the toctrees that
+ are newly included or updated.
+ """
+ for module_name in self.get_new_and_updated_modules():
+ print(module_name)
+
+ def get_modules(self, filename):
+ """
+ Given a filename for a reST file, return an iterator for
+ all of the autogenerated reST files that it includes.
+ """
+ # Create the regular expression used to detect an autogenerated file
+ auto_re = re.compile(r'^\s*(..\/)*(sage(_docbuild)?\/[\w\/]*)\s*$')
+
+ # Read the lines
+ with open(filename) as f:
+ lines = f.readlines()
+ for line in lines:
+ match = auto_re.match(line)
+ if match:
+ yield match.group(2).replace(os.path.sep, '.')
+
+ def get_module_docstring_title(self, module_name):
+ """
+ Return the title of the module from its docstring.
+ """
+ # Try to import the module
+ try:
+ __import__(module_name)
+ except ImportError as err:
+ logger.error("Warning: Could not import %s %s", module_name, err)
+ return "UNABLE TO IMPORT MODULE"
+ module = sys.modules[module_name]
+
+ # Get the docstring
+ doc = module.__doc__
+ if doc is None:
+ doc = module.doc if hasattr(module, 'doc') else ""
+
+ # Extract the title
+ i = doc.find('\n')
+ if i != -1:
+ return doc[i + 1:].lstrip().splitlines()[0]
+ else:
+ return doc
+
+ def auto_rest_filename(self, module_name):
+ """
+ Return the name of the file associated to a given module
+
+ EXAMPLES::
+
+ sage: from sage_docbuild.builders import ReferenceSubBuilder
+ sage: ReferenceSubBuilder("reference").auto_rest_filename("sage.combinat.partition")
+ '.../en/reference/sage/combinat/partition.rst'
+ """
+ return self.dir + os.path.sep + module_name.replace('.', os.path.sep) + '.rst'
+
+ def write_auto_rest_file(self, module_name):
+ """
+ Write the autogenerated reST file for module_name.
+ """
+ if not module_name.startswith('sage'):
+ return
+ filename = self.auto_rest_filename(module_name)
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
+
+ title = self.get_module_docstring_title(module_name)
+
+ if title == '':
+ logger.error("Warning: Missing title for %s", module_name)
+ title = "MISSING TITLE"
+
+ with open(filename, 'w') as outfile:
+ # Don't doctest the autogenerated file.
+ outfile.write(".. nodoctest\n\n")
+ # Now write the actual content.
+ outfile.write(".. _%s:\n\n" % (module_name.replace(".__init__", "")))
+ outfile.write(title + '\n')
+ outfile.write('=' * len(title) + "\n\n")
+ outfile.write('.. This file has been autogenerated.\n\n')
+
+ inherited = ':inherited-members:' if self._options.inherited else ''
+
+ automodule = '''
+.. automodule:: %s
+ :members:
+ :undoc-members:
+ :show-inheritance:
+ %s
+
+'''
+ outfile.write(automodule % (module_name, inherited))
+
+ def clean_auto(self):
+ """
+ Remove all autogenerated reST files.
+ """
+ try:
+ shutil.rmtree(os.path.join(self.dir, 'sage'))
+ logger.debug("Deleted auto-generated reST files in: %s",
+ os.path.join(self.dir, 'sage'))
+ except OSError:
+ pass
+
+ def get_unincluded_modules(self):
+ """
+ Return an iterator for all the modules in the Sage library
+ which are not included in the reference manual.
+ """
+ # Make a dictionary of the included modules
+ included_modules = {}
+ for module_name in self.get_all_included_modules():
+ included_modules[module_name] = True
+
+ base_path = os.path.join(SAGE_SRC, 'sage')
+ for directory, subdirs, files in os.walk(base_path):
+ for filename in files:
+ if not (filename.endswith('.py') or
+ filename.endswith('.pyx')):
+ continue
+
+ path = os.path.join(directory, filename)
+
+ # Create the module name
+ module_name = path[len(base_path):].replace(os.path.sep, '.')
+ module_name = 'sage' + module_name
+ module_name = module_name[:-4] if module_name.endswith('pyx') else module_name[:-3]
+
+ # Exclude some ones -- we don't want init the manual
+ if module_name.endswith('__init__') or module_name.endswith('all'):
+ continue
+
+ if module_name not in included_modules:
+ yield module_name
+
+ def print_unincluded_modules(self):
+ """
+ Print all of the modules which are not included in the Sage
+ reference manual.
+ """
+ for module_name in self.get_unincluded_modules():
+ print(module_name)
+
+ def print_included_modules(self):
+ """
+ Print all of the modules that are included in the Sage reference
+ manual.
+ """
+ for module_name in self.get_all_included_modules():
+ print(module_name)
+
+
+class SingleFileBuilder(DocBuilder):
+ """
+ This is the class used to build the documentation for a single
+ user-specified file. If the file is called 'foo.py', then the
+ documentation is built in ``DIR/foo/`` if the user passes the
+ command line option "-o DIR", or in ``DOT_SAGE/docbuild/foo/``
+ otherwise.
+ """
+ def __init__(self, path):
+ """
+ INPUT:
+
+ - ``path`` - the path to the file for which documentation
+ should be built
+ """
+ self.lang = 'en'
+ self.name = 'single_file'
+ path = os.path.abspath(path)
+
+ # Create docbuild and relevant subdirectories, e.g.,
+ # the static and templates directories in the output directory.
+ # By default, this is DOT_SAGE/docbuild/MODULE_NAME, but can
+ # also be specified at the command line.
+ module_name = os.path.splitext(os.path.basename(path))[0]
+ latex_name = module_name.replace('_', r'\\_')
+
+ if self._options.output_dir:
+ base_dir = os.path.join(self._options.output_dir, module_name)
+ if os.path.exists(base_dir):
+ logger.warning('Warning: Directory %s exists. It is safer to build in a new directory.' % base_dir)
+ else:
+ base_dir = os.path.join(DOT_SAGE, 'docbuild', module_name)
+ try:
+ shutil.rmtree(base_dir)
+ except OSError:
+ pass
+ self.dir = os.path.join(base_dir, 'source')
+
+ os.makedirs(os.path.join(self.dir, "static"), exist_ok=True)
+ os.makedirs(os.path.join(self.dir, "templates"), exist_ok=True)
+ # Write self.dir/conf.py
+ conf = r"""# This file is automatically generated by {}, do not edit!
+
+import sys, os, contextlib
+sys.path.append({!r})
+
+from sage.docs.conf import *
+html_static_path = [] + html_common_static_path
+
+project = 'Documentation for {}'
+release = 'unknown'
+name = {!r}
+html_title = project
+html_short_title = project
+htmlhelp_basename = name
+
+with contextlib.suppress(ValueError):
+ extensions.remove('multidocs') # see #29651
+ extensions.remove('inventory_builder')
+
+latex_domain_indices = False
+latex_documents = [
+ ('index', name + '.tex', 'Documentation for {}',
+ 'unknown', 'manual'),
+]
+""".format(__file__, self.dir, module_name, module_name, latex_name)
+
+ if 'SAGE_DOC_UNDERSCORE' in os.environ:
+ conf += r"""
+def setup(app):
+ app.connect('autodoc-skip-member', skip_member)
+"""
+
+ with open(os.path.join(self.dir, 'conf.py'), 'w') as conffile:
+ conffile.write(conf)
+
+ # Write self.dir/index.rst
+ title = 'Docs for file %s' % path
+ heading = title + "\n" + ("=" * len(title))
+ index = r"""{}
+
+.. This file is automatically generated by {}, do not edit!
+
+.. automodule:: {}
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+""".format(heading, __file__, module_name)
+ with open(os.path.join(self.dir, 'index.rst'), 'w') as indexfile:
+ indexfile.write(index)
+
+ # Create link from original file to self.dir. Note that we
+ # append self.dir to sys.path in conf.py. This is reasonably
+ # safe (but not perfect), since we just created self.dir.
+ try:
+ os.symlink(path, os.path.join(self.dir, os.path.basename(path)))
+ except OSError:
+ pass
+
+ def _output_dir(self, type):
+ """
+ Return the directory where the output of type ``type`` is stored.
+
+ If the directory does not exist, then it will automatically be
+ created.
+ """
+ base_dir = os.path.split(self.dir)[0]
+ d = os.path.join(base_dir, "output", type)
+ os.makedirs(d, exist_ok=True)
+ return d
+
+ def _doctrees_dir(self):
+ """
+ Return the directory where the doctrees are stored.
+
+ If the directory does not exist, then it will automatically be
+ created.
+ """
+ return self._output_dir('doctrees')
+
+
+def get_builder(name):
+ """
+ Return an appropriate *Builder* object for the document ``name``.
+
+ DocBuilder and its subclasses do all the real work in building the
+ documentation.
+ """
+ if name == 'all':
+ from sage.misc.superseded import deprecation
+ deprecation(31948, 'avoid using "sage --docbuild all html" and "sage --docbuild all pdf"; '
+ 'use "make doc" and "make doc-pdf" instead, if available.')
+ return AllBuilder()
+ elif name == 'reference_top':
+ return ReferenceTopBuilder('reference')
+ elif name.endswith('reference'):
+ return ReferenceBuilder(name)
+ elif 'reference' in name and os.path.exists(os.path.join(SAGE_DOC_SRC, 'en', name)):
+ return ReferenceSubBuilder(name)
+ elif name.endswith('website'):
+ return WebsiteBuilder(name)
+ elif name.startswith('file='):
+ path = name[5:]
+ if path.endswith('.sage') or path.endswith('.pyx'):
+ raise NotImplementedError('Building documentation for a single file only works for Python files.')
+ return SingleFileBuilder(path)
+ elif name in get_documents() or name in AllBuilder().get_all_documents():
+ return DocBuilder(name)
+ else:
+ print("'%s' is not a recognized document. Type 'sage --docbuild -D' for a list" % name)
+ print("of documents, or 'sage --docbuild --help' for more help.")
+ sys.exit(1)
+
+
+def get_documents():
+ """
+ Return a list of document names the Sage documentation builder
+ will accept as command-line arguments.
+ """
+ all_b = AllBuilder()
+ docs = all_b.get_all_documents()
+ docs = [(d[3:] if d[0:3] == 'en/' else d) for d in docs]
+ return docs
diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py
new file mode 100644
index 00000000000..13c969a0ec8
--- /dev/null
+++ b/src/sage_docbuild/conf.py
@@ -0,0 +1,984 @@
+import sys
+import os
+import sphinx
+from sage.env import SAGE_DOC_SRC, SAGE_DOC, THEBE_DIR, PPLPY_DOCS, MATHJAX_DIR
+from sage.misc.latex_macros import sage_mathjax_macros
+import sage.version
+from sage.misc.sagedoc import extlinks
+import dateutil.parser
+from docutils import nodes
+from docutils.transforms import Transform
+from sphinx.ext.doctest import blankline_re
+from sphinx import highlighting
+import sphinx.ext.intersphinx as intersphinx
+from IPython.lib.lexers import IPythonConsoleLexer, IPyLexer
+
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+ 'sage_docbuild.ext.inventory_builder',
+ 'sage_docbuild.ext.multidocs',
+ 'sage_docbuild.ext.sage_autodoc',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.extlinks',
+ 'sphinx.ext.mathjax',
+ 'IPython.sphinxext.ipython_directive',
+ 'matplotlib.sphinxext.plot_directive',
+ 'jupyter_sphinx',
+]
+
+jupyter_execute_default_kernel = 'sagemath'
+
+jupyter_sphinx_thebelab_config = {
+ 'requestKernel': True,
+ 'binderOptions': {
+ 'repo': "sagemath/sage-binder-env",
+ },
+ 'kernelOptions': {
+ 'name': "sagemath",
+ 'kernelName': "sagemath",
+ 'path': ".",
+ },
+}
+
+# This code is executed before each ".. PLOT::" directive in the Sphinx
+# documentation. It defines a 'sphinx_plot' function that displays a Sage object
+# through matplotlib, so that it will be displayed in the HTML doc
+plot_html_show_source_link = False
+plot_pre_code = r"""
+# Set locale to prevent having commas in decimal numbers
+# in tachyon input (see https://trac.sagemath.org/ticket/28971)
+import locale
+locale.setlocale(locale.LC_NUMERIC, 'C')
+def sphinx_plot(graphics, **kwds):
+ import matplotlib.image as mpimg
+ import matplotlib.pyplot as plt
+ from sage.misc.temporary_file import tmp_filename
+ from sage.plot.graphics import _parse_figsize
+ if os.environ.get('SAGE_SKIP_PLOT_DIRECTIVE', 'no') != 'yes':
+ ## Option handling is taken from Graphics.save
+ options = dict()
+ if isinstance(graphics, sage.plot.graphics.Graphics):
+ options.update(sage.plot.graphics.Graphics.SHOW_OPTIONS)
+ options.update(graphics._extra_kwds)
+ options.update(kwds)
+ elif isinstance(graphics, sage.plot.multigraphics.MultiGraphics):
+ options.update(kwds)
+ else:
+ graphics = graphics.plot(**kwds)
+ dpi = options.pop('dpi', None)
+ transparent = options.pop('transparent', None)
+ fig_tight = options.pop('fig_tight', None)
+ figsize = options.pop('figsize', None)
+ if figsize is not None:
+ figsize = _parse_figsize(figsize)
+ plt.figure(figsize=figsize)
+ figure = plt.gcf()
+ if isinstance(graphics, (sage.plot.graphics.Graphics,
+ sage.plot.multigraphics.MultiGraphics)):
+ graphics.matplotlib(figure=figure, figsize=figsize, **options)
+ if isinstance(graphics, (sage.plot.graphics.Graphics,
+ sage.plot.multigraphics.GraphicsArray)):
+ # for Graphics and GraphicsArray, tight_layout adjusts the
+ # *subplot* parameters so ticks aren't cut off, etc.
+ figure.tight_layout()
+ else:
+ # 3d graphics via png
+ import matplotlib as mpl
+ mpl.rcParams['image.interpolation'] = 'bilinear'
+ mpl.rcParams['image.resample'] = False
+ mpl.rcParams['figure.figsize'] = [8.0, 6.0]
+ mpl.rcParams['figure.dpi'] = 80
+ mpl.rcParams['savefig.dpi'] = 100
+ fn = tmp_filename(ext=".png")
+ graphics.save(fn)
+ img = mpimg.imread(fn)
+ plt.imshow(img)
+ plt.axis("off")
+ plt.margins(0)
+ if not isinstance(graphics, sage.plot.multigraphics.MultiGraphics):
+ plt.tight_layout(pad=0)
+
+from sage.all_cmdline import *
+"""
+
+plot_html_show_formats = False
+plot_formats = ['svg', 'pdf', 'png']
+
+# We do *not* fully initialize intersphinx since we call it by hand
+# in find_sage_dangling_links.
+#, 'sphinx.ext.intersphinx']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = [os.path.join(SAGE_DOC_SRC, 'common', 'templates'), 'templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = ""
+copyright = "2005--{}, The Sage Development Team".format(dateutil.parser.parse(sage.version.date).year)
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+version = sage.version.version
+release = sage.version.version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of glob-style patterns that should be excluded when looking for
+# source files. [1] They are matched against the source file names
+# relative to the source directory, using slashes as directory
+# separators on all platforms.
+exclude_patterns = ['.build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+default_role = 'math'
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use. NOTE:
+# This overrides a HTML theme's corresponding setting (see below).
+pygments_style = 'sphinx'
+
+# Default lexer to use when highlighting code blocks, using the IPython
+# console lexers. 'ipycon' is the IPython console, which is what we want
+# for most code blocks: anything with "sage:" prompts. For other IPython,
+# like blocks which might appear in a notebook cell, use 'ipython'.
+highlighting.lexers['ipycon'] = IPythonConsoleLexer(in1_regex=r'sage: ', in2_regex=r'[.][.][.][.]: ')
+highlighting.lexers['ipython'] = IPyLexer()
+highlight_language = 'ipycon'
+
+# Extension configuration
+# -----------------------
+
+# include the todos
+todo_include_todos = True
+
+# Cross-links to other project's online documentation.
+python_version = sys.version_info.major
+
+def set_intersphinx_mappings(app, config):
+ """
+ Add precompiled inventory (the objects.inv)
+ """
+ refpath = os.path.join(SAGE_DOC, "html", "en", "reference")
+ invpath = os.path.join(SAGE_DOC, "inventory", "en", "reference")
+ if app.config.multidoc_first_pass == 1 or \
+ not (os.path.exists(refpath) and os.path.exists(invpath)):
+ app.config.intersphinx_mapping = {}
+ return
+
+ app.config.intersphinx_mapping = {
+ 'python': ('https://docs.python.org/',
+ os.path.join(SAGE_DOC_SRC, "common",
+ "python{}.inv".format(python_version))),
+ 'pplpy': (PPLPY_DOCS, None)}
+
+ # Add master intersphinx mapping
+ dst = os.path.join(invpath, 'objects.inv')
+ app.config.intersphinx_mapping['sagemath'] = (refpath, dst)
+
+ # Add intersphinx mapping for subdirectories
+ # We intentionally do not name these such that these get higher
+ # priority in case of conflicts
+ for directory in os.listdir(os.path.join(invpath)):
+ if directory == 'jupyter_execute':
+ # This directory is created by jupyter-sphinx extension for
+ # internal use and should be ignored here. See trac #33507.
+ continue
+ if os.path.isdir(os.path.join(invpath, directory)):
+ src = os.path.join(refpath, directory)
+ dst = os.path.join(invpath, directory, 'objects.inv')
+ app.config.intersphinx_mapping[src] = dst
+
+ intersphinx.normalize_intersphinx_mapping(app, config)
+
+# By default document are not master.
+multidocs_is_master = True
+
+# Options for HTML output
+# -----------------------
+
+# Sage default HTML theme. We use a custom theme to set a Pygments style,
+# stylesheet, and insert MathJax macros. See the directory
+# doc/common/themes/sage-classic/ for files comprising the custom theme.
+html_theme = 'sage-classic'
+
+# Theme options are theme-specific and customize the look and feel of
+# a theme further. For a list of options available for each theme,
+# see the documentation.
+html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = [os.path.join(SAGE_DOC_SRC, 'common', 'themes')]
+
+# HTML style sheet NOTE: This overrides a HTML theme's corresponding
+# setting.
+#html_style = 'default.css'
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+#html_logo = 'sagelogo-word.ico'
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+html_favicon = 'favicon.ico'
+
+# html_static_path defined here and imported in the actual configuration file
+# conf.py read by Sphinx was the cause of subtle bugs in builders (see #30418 for
+# instance). Hence now html_common_static_path contains the common paths to static
+# files, and is combined to html_static_path in each conf.py file read by Sphinx.
+html_common_static_path = [os.path.join(SAGE_DOC_SRC, 'common', 'static'),
+ THEBE_DIR, 'static']
+
+# Configure MathJax
+# https://docs.mathjax.org/en/latest/options/input/tex.html
+mathjax3_config = {
+ "tex": {
+ # Add custom sage macros
+ # http://docs.mathjax.org/en/latest/input/tex/macros.html
+ "macros": sage_mathjax_macros(),
+ # Add $...$ as possible inline math
+ # https://docs.mathjax.org/en/latest/input/tex/delimiters.html#tex-and-latex-math-delimiters
+ "inlineMath": [["$", "$"], ["\\(", "\\)"]],
+ # Increase the limit the size of the string to be processed
+ # https://docs.mathjax.org/en/latest/options/input/tex.html#option-descriptions
+ "maxBuffer": 50 * 1024,
+ # Use colorv2 extension instead of built-in color extension
+ # https://docs.mathjax.org/en/latest/input/tex/extensions/autoload.html#tex-autoload-options
+ # https://docs.mathjax.org/en/latest/input/tex/extensions/colorv2.html#tex-colorv2
+ "autoload": {"color": [], "colorv2": ["color"]},
+ },
+}
+
+if os.environ.get('SAGE_USE_CDNS', 'no') == 'yes':
+ mathjax_path = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"
+else:
+ mathjax_path = 'mathjax/tex-chtml.js'
+ html_common_static_path += [MATHJAX_DIR]
+
+# A list of glob-style patterns that should be excluded when looking for source
+# files. They are matched against the source file names relative to the
+# source directory, using slashes as directory separators on all platforms.
+exclude_patterns = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# A list of prefixes that are ignored for sorting the Python module index ( if
+# this is set to ['foo.'], then foo.bar is shown under B, not F). Works only
+# for the HTML builder currently.
+modindex_common_prefix = ['sage.']
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+html_split_index = True
+
+# If true, the reST sources are included in the HTML build as _sources/.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+#htmlhelp_basename = ''
+
+# Options for LaTeX output
+# ------------------------
+# See http://sphinx-doc.org/config.html#confval-latex_elements
+latex_elements = {}
+
+# The paper size ('letterpaper' or 'a4paper').
+#latex_elements['papersize'] = 'letterpaper'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_elements['pointsize'] = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = []
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = 'sagelogo-word.png'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+latex_elements['preamble'] = r"""
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage{textcomp}
+\usepackage{mathrsfs}
+\usepackage{iftex}
+
+% Only declare unicode characters when compiling with pdftex; E.g. japanese
+% tutorial does not use pdftex
+\ifPDFTeX
+ \DeclareUnicodeCharacter{01CE}{\capitalcaron a}
+ \DeclareUnicodeCharacter{0428}{cyrillic Sha}
+ \DeclareUnicodeCharacter{250C}{+}
+ \DeclareUnicodeCharacter{2510}{+}
+ \DeclareUnicodeCharacter{2514}{+}
+ \DeclareUnicodeCharacter{2518}{+}
+ \DeclareUnicodeCharacter{253C}{+}
+
+ \DeclareUnicodeCharacter{03B1}{\ensuremath{\alpha}}
+ \DeclareUnicodeCharacter{03B2}{\ensuremath{\beta}}
+ \DeclareUnicodeCharacter{03B3}{\ensuremath{\gamma}}
+ \DeclareUnicodeCharacter{0393}{\ensuremath{\Gamma}}
+ \DeclareUnicodeCharacter{03B4}{\ensuremath{\delta}}
+ \DeclareUnicodeCharacter{0394}{\ensuremath{\Delta}}
+ \DeclareUnicodeCharacter{03B5}{\ensuremath{\varepsilon}}
+ \DeclareUnicodeCharacter{03B6}{\ensuremath{\zeta}}
+ \DeclareUnicodeCharacter{03B7}{\ensuremath{\eta}}
+ \DeclareUnicodeCharacter{03B8}{\ensuremath{\vartheta}}
+ \DeclareUnicodeCharacter{0398}{\ensuremath{\Theta}}
+ \DeclareUnicodeCharacter{03BA}{\ensuremath{\kappa}}
+ \DeclareUnicodeCharacter{03BB}{\ensuremath{\lambda}}
+ \DeclareUnicodeCharacter{039B}{\ensuremath{\Lambda}}
+ \DeclareUnicodeCharacter{00B5}{\ensuremath{\mu}} % micron sign
+ \DeclareUnicodeCharacter{03BC}{\ensuremath{\mu}}
+ \DeclareUnicodeCharacter{03BD}{\ensuremath{\nu}}
+ \DeclareUnicodeCharacter{03BE}{\ensuremath{\xi}}
+ \DeclareUnicodeCharacter{039E}{\ensuremath{\Xi}}
+ \DeclareUnicodeCharacter{03B9}{\ensuremath{\iota}}
+ \DeclareUnicodeCharacter{03C0}{\ensuremath{\pi}}
+ \DeclareUnicodeCharacter{03A0}{\ensuremath{\Pi}}
+ \DeclareUnicodeCharacter{03C1}{\ensuremath{\rho}}
+ \DeclareUnicodeCharacter{03C3}{\ensuremath{\sigma}}
+ \DeclareUnicodeCharacter{03A3}{\ensuremath{\Sigma}}
+ \DeclareUnicodeCharacter{03C4}{\ensuremath{\tau}}
+ \DeclareUnicodeCharacter{03C6}{\ensuremath{\varphi}}
+ \DeclareUnicodeCharacter{03A6}{\ensuremath{\Phi}}
+ \DeclareUnicodeCharacter{03C7}{\ensuremath{\chi}}
+ \DeclareUnicodeCharacter{03C8}{\ensuremath{\psi}}
+ \DeclareUnicodeCharacter{03A8}{\ensuremath{\Psi}}
+ \DeclareUnicodeCharacter{03C9}{\ensuremath{\omega}}
+ \DeclareUnicodeCharacter{03A9}{\ensuremath{\Omega}}
+ \DeclareUnicodeCharacter{03C5}{\ensuremath{\upsilon}}
+ \DeclareUnicodeCharacter{03A5}{\ensuremath{\Upsilon}}
+ \DeclareUnicodeCharacter{2113}{\ell}
+
+ \DeclareUnicodeCharacter{2148}{\ensuremath{\id}}
+ \DeclareUnicodeCharacter{2202}{\ensuremath{\partial}}
+ \DeclareUnicodeCharacter{2205}{\ensuremath{\emptyset}}
+ \DeclareUnicodeCharacter{2208}{\ensuremath{\in}}
+ \DeclareUnicodeCharacter{2209}{\ensuremath{\notin}}
+ \DeclareUnicodeCharacter{2211}{\ensuremath{\sum}}
+ \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}}
+ \DeclareUnicodeCharacter{221E}{\ensuremath{\infty}}
+ \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}}
+ \DeclareUnicodeCharacter{2228}{\ensuremath{\vee}}
+ \DeclareUnicodeCharacter{2229}{\ensuremath{\cap}}
+ \DeclareUnicodeCharacter{222A}{\ensuremath{\cup}}
+ \DeclareUnicodeCharacter{222B}{\ensuremath{\int}}
+ \DeclareUnicodeCharacter{2248}{\ensuremath{\approx}}
+ \DeclareUnicodeCharacter{2260}{\ensuremath{\neq}}
+ \DeclareUnicodeCharacter{2264}{\ensuremath{\leq}}
+ \DeclareUnicodeCharacter{2265}{\ensuremath{\geq}}
+ \DeclareUnicodeCharacter{2293}{\ensuremath{\sqcap}}
+ \DeclareUnicodeCharacter{2294}{\ensuremath{\sqcup}}
+ \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}}
+ \DeclareUnicodeCharacter{22C1}{\ensuremath{\bigvee}}
+ \DeclareUnicodeCharacter{22C2}{\ensuremath{\bigcap}}
+ \DeclareUnicodeCharacter{22C3}{\ensuremath{\bigcup}}
+ \DeclareUnicodeCharacter{2323}{\ensuremath{\smile}} % cup product
+ \DeclareUnicodeCharacter{00B1}{\ensuremath{\pm}}
+ \DeclareUnicodeCharacter{2A02}{\ensuremath{\bigotimes}}
+ \DeclareUnicodeCharacter{2297}{\ensuremath{\otimes}}
+ \DeclareUnicodeCharacter{2A01}{\ensuremath{\oplus}}
+ \DeclareUnicodeCharacter{00BD}{\ensuremath{\nicefrac{1}{2}}}
+ \DeclareUnicodeCharacter{00D7}{\ensuremath{\times}}
+ \DeclareUnicodeCharacter{00B7}{\ensuremath{\cdot}}
+ \DeclareUnicodeCharacter{230A}{\ensuremath{\lfloor}}
+ \DeclareUnicodeCharacter{230B}{\ensuremath{\rfloor}}
+ \DeclareUnicodeCharacter{2308}{\ensuremath{\lceil}}
+ \DeclareUnicodeCharacter{2309}{\ensuremath{\rceil}}
+ \DeclareUnicodeCharacter{22C5}{\ensuremath{\cdot}}
+ \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}}
+ \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}}
+ \DeclareUnicodeCharacter{2192}{\ensuremath{\to}}
+ \DeclareUnicodeCharacter{21A6}{\ensuremath{\mapsto}}
+ \DeclareUnicodeCharacter{2102}{\ensuremath{\mathbb{C}}}
+ \DeclareUnicodeCharacter{211A}{\ensuremath{\mathbb{Q}}}
+ \DeclareUnicodeCharacter{211D}{\ensuremath{\mathbb{R}}}
+ \DeclareUnicodeCharacter{2124}{\ensuremath{\mathbb{Z}}}
+ \DeclareUnicodeCharacter{2202}{\ensuremath{\partial}}
+
+ \DeclareUnicodeCharacter{2070}{\ensuremath{{}^0}}
+ \DeclareUnicodeCharacter{00B9}{\ensuremath{{}^1}}
+ \DeclareUnicodeCharacter{00B2}{\ensuremath{{}^2}}
+ \DeclareUnicodeCharacter{00B3}{\ensuremath{{}^3}}
+ \DeclareUnicodeCharacter{2074}{\ensuremath{{}^4}}
+ \DeclareUnicodeCharacter{2075}{\ensuremath{{}^5}}
+ \DeclareUnicodeCharacter{2076}{\ensuremath{{}^6}}
+ \DeclareUnicodeCharacter{2077}{\ensuremath{{}^7}}
+ \DeclareUnicodeCharacter{2078}{\ensuremath{{}^8}}
+ \DeclareUnicodeCharacter{2079}{\ensuremath{{}^9}}
+ \DeclareUnicodeCharacter{207A}{\ensuremath{{}^+}}
+ \DeclareUnicodeCharacter{207B}{\ensuremath{{}^-}}
+ \DeclareUnicodeCharacter{141F}{\ensuremath{{}^/}}
+ \DeclareUnicodeCharacter{2080}{\ensuremath{{}_0}}
+ \DeclareUnicodeCharacter{2081}{\ensuremath{{}_1}}
+ \DeclareUnicodeCharacter{2082}{\ensuremath{{}_2}}
+ \DeclareUnicodeCharacter{2083}{\ensuremath{{}_3}}
+ \DeclareUnicodeCharacter{2084}{\ensuremath{{}_4}}
+ \DeclareUnicodeCharacter{2085}{\ensuremath{{}_5}}
+ \DeclareUnicodeCharacter{2086}{\ensuremath{{}_6}}
+ \DeclareUnicodeCharacter{2087}{\ensuremath{{}_7}}
+ \DeclareUnicodeCharacter{2088}{\ensuremath{{}_8}}
+ \DeclareUnicodeCharacter{2089}{\ensuremath{{}_9}}
+ \DeclareUnicodeCharacter{208A}{\ensuremath{{}_+}}
+ \DeclareUnicodeCharacter{208B}{\ensuremath{{}_-}}
+ \DeclareUnicodeCharacter{1D62}{\ensuremath{{}_i}}
+ \DeclareUnicodeCharacter{2C7C}{\ensuremath{{}_j}}
+
+ \newcommand{\sageMexSymbol}[1]
+ {{\fontencoding{OMX}\fontfamily{cmex}\selectfont\raisebox{0.75em}{\symbol{#1}}}}
+ \DeclareUnicodeCharacter{239B}{\sageMexSymbol{"30}} % parenlefttp
+ \DeclareUnicodeCharacter{239C}{\sageMexSymbol{"42}} % parenleftex
+ \DeclareUnicodeCharacter{239D}{\sageMexSymbol{"40}} % parenleftbt
+ \DeclareUnicodeCharacter{239E}{\sageMexSymbol{"31}} % parenrighttp
+ \DeclareUnicodeCharacter{239F}{\sageMexSymbol{"43}} % parenrightex
+ \DeclareUnicodeCharacter{23A0}{\sageMexSymbol{"41}} % parenrightbt
+ \DeclareUnicodeCharacter{23A1}{\sageMexSymbol{"32}} % bracketlefttp
+ \DeclareUnicodeCharacter{23A2}{\sageMexSymbol{"36}} % bracketleftex
+ \DeclareUnicodeCharacter{23A3}{\sageMexSymbol{"34}} % bracketleftbt
+ \DeclareUnicodeCharacter{23A4}{\sageMexSymbol{"33}} % bracketrighttp
+ \DeclareUnicodeCharacter{23A5}{\sageMexSymbol{"37}} % bracketrightex
+ \DeclareUnicodeCharacter{23A6}{\sageMexSymbol{"35}} % bracketrightbt
+
+ \DeclareUnicodeCharacter{23A7}{\sageMexSymbol{"38}} % curly brace left top
+ \DeclareUnicodeCharacter{23A8}{\sageMexSymbol{"3C}} % curly brace left middle
+ \DeclareUnicodeCharacter{23A9}{\sageMexSymbol{"3A}} % curly brace left bottom
+ \DeclareUnicodeCharacter{23AA}{\sageMexSymbol{"3E}} % curly brace extension
+ \DeclareUnicodeCharacter{23AB}{\sageMexSymbol{"39}} % curly brace right top
+ \DeclareUnicodeCharacter{23AC}{\sageMexSymbol{"3D}} % curly brace right middle
+ \DeclareUnicodeCharacter{23AD}{\sageMexSymbol{"3B}} % curly brace right bottom
+ \DeclareUnicodeCharacter{23B0}{\{} % 2-line curly brace left top half (not in cmex)
+ \DeclareUnicodeCharacter{23B1}{\}} % 2-line curly brace right top half (not in cmex)
+
+ \DeclareUnicodeCharacter{2320}{\ensuremath{\int}} % top half integral
+ \DeclareUnicodeCharacter{2321}{\ensuremath{\int}} % bottom half integral
+ \DeclareUnicodeCharacter{23AE}{\ensuremath{\|}} % integral extenison
+
+ % Box drawings light
+ \DeclareUnicodeCharacter{2500}{-} % h
+ \DeclareUnicodeCharacter{2502}{|} % v
+ \DeclareUnicodeCharacter{250C}{+} % dr
+ \DeclareUnicodeCharacter{2510}{+} % dl
+ \DeclareUnicodeCharacter{2514}{+} % ur
+ \DeclareUnicodeCharacter{2518}{+} % ul
+ \DeclareUnicodeCharacter{251C}{+} % vr
+ \DeclareUnicodeCharacter{2524}{+} % vl
+ \DeclareUnicodeCharacter{252C}{+} % dh
+ \DeclareUnicodeCharacter{2534}{+} % uh
+ \DeclareUnicodeCharacter{253C}{+} % vh
+ \DeclareUnicodeCharacter{2571}{/} % upper right to lower left
+ \DeclareUnicodeCharacter{2571}{\setminus} % upper left to lower right
+
+ \DeclareUnicodeCharacter{25CF}{\ensuremath{\bullet}} % medium black circle
+ \DeclareUnicodeCharacter{26AC}{\ensuremath{\circ}} % medium small white circle
+ \DeclareUnicodeCharacter{256D}{+}
+ \DeclareUnicodeCharacter{256E}{+}
+ \DeclareUnicodeCharacter{256F}{+}
+ \DeclareUnicodeCharacter{2570}{+}
+\fi
+
+\let\textLaTeX\LaTeX
+\AtBeginDocument{\renewcommand*{\LaTeX}{\hbox{\textLaTeX}}}
+
+% Workaround for a LaTeX bug -- see trac #31397 and
+% https://tex.stackexchange.com/questions/583391/mactex-2020-error-with-report-hyperref-mathbf-in-chapter.
+\makeatletter
+\pdfstringdefDisableCommands{%
+ \let\mathbf\@firstofone
+}
+\makeatother
+"""
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+#####################################################
+# add LaTeX macros for Sage
+
+from sage.misc.latex_macros import sage_latex_macros
+
+try:
+ pngmath_latex_preamble # check whether this is already defined
+except NameError:
+ pngmath_latex_preamble = ""
+
+for macro in sage_latex_macros():
+ # used when building latex and pdf versions
+ latex_elements['preamble'] += macro + '\n'
+ # used when building html version
+ pngmath_latex_preamble += macro + '\n'
+
+#####################################################
+# add custom context variables for templates
+
+def add_page_context(app, pagename, templatename, context, doctree):
+ # # The template function
+ # def template_function(arg):
+ # return "Your string is " + arg
+ # # Add it to the page's context
+ # context['template_function'] = template_function
+ path1 = os.path.dirname(app.builder.get_outfilename(pagename))
+ path2 = os.path.join(SAGE_DOC, 'html', 'en')
+ relpath = os.path.relpath(path2, path1)
+ context['release'] = release
+ context['documentation_title'] = 'Sage {}'.format(release) + ' Documentation'
+ context['documentation_root'] = os.path.join(relpath, 'index.html')
+ if 'website' in path1:
+ context['title'] = 'Documentation'
+ context['website'] = True
+
+ if 'reference' in path1 and not path1.endswith('reference'):
+ path2 = os.path.join(SAGE_DOC, 'html', 'en', 'reference')
+ relpath = os.path.relpath(path2, path1)
+ context['reference_title'] = 'Reference Manual'
+ context['reference_root'] = os.path.join(relpath, 'index.html')
+ context['refsub'] = True
+
+#####################################################
+
+def process_docstring_aliases(app, what, name, obj, options, docstringlines):
+ """
+ Change the docstrings for aliases to point to the original object.
+ """
+ basename = name.rpartition('.')[2]
+ if hasattr(obj, '__name__') and obj.__name__ != basename:
+ docstringlines[:] = ['See :obj:`%s`.' % name]
+
+def process_directives(app, what, name, obj, options, docstringlines):
+ """
+ Remove 'nodetex' and other directives from the first line of any
+ docstring where they appear.
+ """
+ if len(docstringlines) == 0:
+ return
+ first_line = docstringlines[0]
+ directives = [ d.lower() for d in first_line.split(',') ]
+ if 'nodetex' in directives:
+ docstringlines.pop(0)
+
+def process_docstring_cython(app, what, name, obj, options, docstringlines):
+ """
+ Remove Cython's filename and location embedding.
+ """
+ if len(docstringlines) <= 1:
+ return
+
+ first_line = docstringlines[0]
+ if first_line.startswith('File:') and '(starting at' in first_line:
+ #Remove the first two lines
+ docstringlines.pop(0)
+ docstringlines.pop(0)
+
+def process_docstring_module_title(app, what, name, obj, options, docstringlines):
+ """
+ Removes the first line from the beginning of the module's docstring. This
+ corresponds to the title of the module's documentation page.
+ """
+ if what != "module":
+ return
+
+ #Remove any additional blank lines at the beginning
+ title_removed = False
+ while len(docstringlines) > 1 and not title_removed:
+ if docstringlines[0].strip() != "":
+ title_removed = True
+ docstringlines.pop(0)
+
+ #Remove any additional blank lines at the beginning
+ while len(docstringlines) > 1:
+ if docstringlines[0].strip() == "":
+ docstringlines.pop(0)
+ else:
+ break
+
+skip_picklability_check_modules = [
+ #'sage.misc.test_nested_class', # for test only
+ 'sage.misc.latex',
+ 'sage.misc.explain_pickle',
+ '__builtin__',
+]
+
+def check_nested_class_picklability(app, what, name, obj, skip, options):
+ """
+ Print a warning if pickling is broken for nested classes.
+ """
+ if hasattr(obj, '__dict__') and hasattr(obj, '__module__'):
+ # Check picklability of nested classes. Adapted from
+ # sage.misc.nested_class.modify_for_nested_pickle.
+ module = sys.modules[obj.__module__]
+ for (nm, v) in obj.__dict__.items():
+ if (isinstance(v, type) and
+ v.__name__ == nm and
+ v.__module__ == module.__name__ and
+ getattr(module, nm, None) is not v and
+ v.__module__ not in skip_picklability_check_modules):
+ # OK, probably this is an *unpicklable* nested class.
+ app.warn('Pickling of nested class %r is probably broken. '
+ 'Please set the metaclass of the parent class to '
+ 'sage.misc.nested_class.NestedClassMetaclass.' % (
+ v.__module__ + '.' + name + '.' + nm))
+
+
+def skip_member(app, what, name, obj, skip, options):
+ """
+ To suppress Sphinx warnings / errors, we
+
+ - Don't include [aliases of] builtins.
+
+ - Don't include the docstring for any nested class which has been
+ inserted into its module by
+ :class:`sage.misc.NestedClassMetaclass` only for pickling. The
+ class will be properly documented inside its surrounding class.
+
+ - Optionally, check whether pickling is broken for nested classes.
+
+ - Optionally, include objects whose name begins with an underscore
+ ('_'), i.e., "private" or "hidden" attributes, methods, etc.
+
+ Otherwise, we abide by Sphinx's decision. Note: The object
+ ``obj`` is excluded (included) if this handler returns True
+ (False).
+ """
+ if 'SAGE_CHECK_NESTED' in os.environ:
+ check_nested_class_picklability(app, what, name, obj, skip, options)
+
+ if getattr(obj, '__module__', None) == '__builtin__':
+ return True
+
+ objname = getattr(obj, "__name__", None)
+ if objname is not None:
+ # check if name was inserted to the module by NestedClassMetaclass
+ if name.find('.') != -1 and objname.find('.') != -1:
+ if objname.split('.')[-1] == name.split('.')[-1]:
+ return True
+
+ if 'SAGE_DOC_UNDERSCORE' in os.environ:
+ if name.split('.')[-1].startswith('_'):
+ return False
+
+ return skip
+
+
+def process_dollars(app, what, name, obj, options, docstringlines):
+ r"""
+ Replace dollar signs with backticks.
+
+ See sage.misc.sagedoc.process_dollars for more information.
+ """
+ if len(docstringlines) and name.find("process_dollars") == -1:
+ from sage.misc.sagedoc import process_dollars as sagedoc_dollars
+ s = sagedoc_dollars("\n".join(docstringlines))
+ lines = s.split("\n")
+ for i in range(len(lines)):
+ docstringlines[i] = lines[i]
+
+def process_inherited(app, what, name, obj, options, docstringlines):
+ """
+ If we're including inherited members, omit their docstrings.
+ """
+ if not options.get('inherited-members'):
+ return
+
+ if what in ['class', 'data', 'exception', 'function', 'module']:
+ return
+
+ name = name.split('.')[-1]
+
+ if what == 'method' and hasattr(obj, 'im_class'):
+ if name in obj.im_class.__dict__.keys():
+ return
+
+ if what == 'attribute' and hasattr(obj, '__objclass__'):
+ if name in obj.__objclass__.__dict__.keys():
+ return
+
+ for i in range(len(docstringlines)):
+ docstringlines.pop()
+
+dangling_debug = False
+
+def debug_inf(app, message):
+ if dangling_debug:
+ app.info(message)
+
+def call_intersphinx(app, env, node, contnode):
+ r"""
+ Call intersphinx and make links between Sage manuals relative.
+
+ TESTS:
+
+ Check that the link from the thematic tutorials to the reference
+ manual is relative, see :trac:`20118`::
+
+ sage: from sage.env import SAGE_DOC
+ sage: thematic_index = os.path.join(SAGE_DOC, "html", "en", "thematic_tutorials", "index.html")
+ sage: for line in open(thematic_index).readlines(): # optional - sagemath_doc_html
+ ....: if "padics" in line:
+ ....: _ = sys.stdout.write(line)
+
+ """
+ debug_inf(app, "???? Trying intersphinx for %s" % node['reftarget'])
+ builder = app.builder
+ res = intersphinx.missing_reference(
+ app, env, node, contnode)
+ if res:
+ # Replace absolute links to $SAGE_DOC by relative links: this
+ # allows to copy the whole documentation tree somewhere else
+ # without breaking links, see Trac #20118.
+ if res['refuri'].startswith(SAGE_DOC):
+ here = os.path.dirname(os.path.join(builder.outdir,
+ node['refdoc']))
+ res['refuri'] = os.path.relpath(res['refuri'], here)
+ debug_inf(app, "++++ Found at %s" % res['refuri'])
+ else:
+ debug_inf(app, "---- Intersphinx: %s not Found" % node['reftarget'])
+ return res
+
+def find_sage_dangling_links(app, env, node, contnode):
+ r"""
+ Try to find dangling link in local module imports or all.py.
+ """
+ debug_inf(app, "==================== find_sage_dangling_links ")
+
+ reftype = node['reftype']
+ reftarget = node['reftarget']
+ try:
+ doc = node['refdoc']
+ except KeyError:
+ debug_inf(app, "-- no refdoc in node %s" % node)
+ return None
+
+ debug_inf(app, "Searching %s from %s"%(reftarget, doc))
+
+ # Workaround: in Python's doc 'object', 'list', ... are documented as a
+ # function rather than a class
+ if reftarget in base_class_as_func and reftype == 'class':
+ node['reftype'] = 'func'
+
+ res = call_intersphinx(app, env, node, contnode)
+ if res:
+ debug_inf(app, "++ DONE %s"%(res['refuri']))
+ return res
+
+ if node.get('refdomain') != 'py': # not a python file
+ return None
+
+ try:
+ module = node['py:module']
+ cls = node['py:class']
+ except KeyError:
+ debug_inf(app, "-- no module or class for :%s:%s"%(reftype, reftarget))
+ return None
+
+ basename = reftarget.split(".")[0]
+ try:
+ target_module = getattr(sys.modules['sage.all'], basename).__module__
+ debug_inf(app, "++ found %s using sage.all in %s" % (basename, target_module))
+ except AttributeError:
+ try:
+ target_module = getattr(sys.modules[node['py:module']], basename).__module__
+ debug_inf(app, "++ found %s in this module" % (basename,))
+ except AttributeError:
+ debug_inf(app, "-- %s not found in sage.all or this module" % (basename))
+ return None
+ except KeyError:
+ target_module = None
+ if target_module is None:
+ target_module = ""
+ debug_inf(app, "?? found in None !!!")
+
+ newtarget = target_module+'.'+reftarget
+ node['reftarget'] = newtarget
+
+ # adapted from sphinx/domains/python.py
+ builder = app.builder
+ searchmode = node.hasattr('refspecific') and 1 or 0
+ matches = builder.env.domains['py'].find_obj(
+ builder.env, module, cls, newtarget, reftype, searchmode)
+ if not matches:
+ debug_inf(app, "?? no matching doc for %s"%newtarget)
+ return call_intersphinx(app, env, node, contnode)
+ elif len(matches) > 1:
+ env.warn(target_module,
+ 'more than one target found for cross-reference '
+ '%r: %s' % (newtarget,
+ ', '.join(match[0] for match in matches)),
+ node.line)
+ name, obj = matches[0]
+ debug_inf(app, "++ match = %s %s"%(name, obj))
+
+ from docutils import nodes
+ newnode = nodes.reference('', '', internal=True)
+ if name == target_module:
+ newnode['refid'] = name
+ else:
+ newnode['refuri'] = builder.get_relative_uri(node['refdoc'], obj[0])
+ newnode['refuri'] += '#' + name
+ debug_inf(app, "++ DONE at URI %s"%(newnode['refuri']))
+ newnode['reftitle'] = name
+ newnode.append(contnode)
+ return newnode
+
+# lists of basic Python class which are documented as functions
+base_class_as_func = [
+ 'bool', 'complex', 'dict', 'file', 'float',
+ 'frozenset', 'int', 'list', 'long', 'object',
+ 'set', 'slice', 'str', 'tuple', 'type', 'unicode', 'xrange']
+
+# Nit picky option configuration: Put here broken links we want to ignore. For
+# link to the Python documentation several links where broken because there
+# where class listed as functions. Expand the list 'base_class_as_func' above
+# instead of marking the link as broken.
+nitpick_ignore = [
+ ('py:class', 'twisted.web2.resource.Resource'),
+ ('py:class', 'twisted.web2.resource.PostableResource')]
+
+def nitpick_patch_config(app):
+ """
+ Patch the default config for nitpicky
+
+ Calling path_config ensure that nitpicky is not considered as a Sphinx
+ environment variable but rather as a Sage environment variable. As a
+ consequence, changing it doesn't force the recompilation of the entire
+ documentation.
+ """
+ app.config.values['nitpicky'] = (False, 'sage')
+ app.config.values['nitpick_ignore'] = ([], 'sage')
+
+def skip_TESTS_block(app, what, name, obj, options, docstringlines):
+ """
+ Skip blocks labeled "TESTS:".
+
+ See sage.misc.sagedoc.skip_TESTS_block for more information.
+ """
+ from sage.misc.sagedoc import skip_TESTS_block as sagedoc_skip_TESTS
+ if not docstringlines:
+ # No docstring, so don't do anything. See Trac #19932.
+ return
+ s = sagedoc_skip_TESTS("\n".join(docstringlines))
+ lines = s.split("\n")
+ for i in range(len(lines)):
+ docstringlines[i] = lines[i]
+ while len(docstringlines) > len(lines):
+ del docstringlines[len(lines)]
+
+class SagemathTransform(Transform):
+ """
+ Transform for code-blocks.
+
+ This allows Sphinx to treat code-blocks with prompt "sage:" as
+ associated with the pycon lexer, and in particular, to change
+ "" to a blank line.
+ """
+ default_priority = 500
+
+ def apply(self):
+ for node in self.document.traverse(nodes.literal_block):
+ if node.get('language') is None and node.astext().startswith('sage:'):
+ node['language'] = 'ipycon'
+ source = node.rawsource
+ source = blankline_re.sub('', source)
+ node.rawsource = source
+ node[:] = [nodes.Text(source)]
+
+from sage.misc.sageinspect import sage_getargspec
+autodoc_builtin_argspec = sage_getargspec
+
+def setup(app):
+ app.connect('autodoc-process-docstring', process_docstring_cython)
+ app.connect('autodoc-process-docstring', process_directives)
+ app.connect('autodoc-process-docstring', process_docstring_module_title)
+ app.connect('autodoc-process-docstring', process_dollars)
+ app.connect('autodoc-process-docstring', process_inherited)
+ if os.environ.get('SAGE_SKIP_TESTS_BLOCKS', False):
+ app.connect('autodoc-process-docstring', skip_TESTS_block)
+ app.connect('autodoc-skip-member', skip_member)
+ app.add_transform(SagemathTransform)
+
+ # When building the standard docs, app.srcdir is set to SAGE_DOC_SRC +
+ # 'LANGUAGE/DOCNAME', but when doing introspection, app.srcdir is
+ # set to a temporary directory. We don't want to use intersphinx,
+ # etc., when doing introspection.
+ if app.srcdir.startswith(SAGE_DOC_SRC):
+ app.add_config_value('intersphinx_mapping', {}, False)
+ app.add_config_value('intersphinx_cache_limit', 5, False)
+ app.add_config_value('intersphinx_disabled_reftypes', [], False)
+ app.connect('config-inited', set_intersphinx_mappings)
+ app.connect('builder-inited', intersphinx.load_mappings)
+ # We do *not* fully initialize intersphinx since we call it by hand
+ # in find_sage_dangling_links.
+ # app.connect('missing-reference', missing_reference)
+ app.connect('missing-reference', find_sage_dangling_links)
+ app.connect('builder-inited', nitpick_patch_config)
+ app.connect('html-page-context', add_page_context)
+
diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py
index a39c99ffe9f..5c2b20c102a 100644
--- a/src/sage_docbuild/sphinxbuild.py
+++ b/src/sage_docbuild/sphinxbuild.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
r"""
-This is Sage's version of the sphinx-build script
+Sphinx build script
-We redirect stdout and stderr to our own logger, and remove some unwanted chatter.
+This is Sage's version of the ``sphinx-build`` script. We redirect ``stdout`` and
+``stderr`` to our own logger, and remove some unwanted chatter.
"""
# ****************************************************************************
# Copyright (C) 2013-2014 Volker Braun
diff --git a/src/sage_docbuild/utils.py b/src/sage_docbuild/utils.py
index 4d815f9dd4e..196f761650e 100644
--- a/src/sage_docbuild/utils.py
+++ b/src/sage_docbuild/utils.py
@@ -1,4 +1,6 @@
-"""Miscellaneous utilities for running the docbuilder."""
+r"""
+Utilities
+"""
import errno
import os
@@ -96,7 +98,7 @@ def build_many(target, args, processes=None):
This is a simplified version of ``multiprocessing.Pool.map`` from the
Python standard library which avoids a couple of its pitfalls. In
- particular, it can abort (with a `RuntimeError`) without hanging if one of
+ particular, it can abort (with a ``RuntimeError``) without hanging if one of
the worker processes unexpectedly dies. It also has semantics equivalent
to ``maxtasksperchild=1``; that is, one process is started per argument.
As such, this is inefficient for processing large numbers of fast tasks,
@@ -106,12 +108,12 @@ def build_many(target, args, processes=None):
It also avoids starting new processes from a pthread, which results in at
least two known issues:
- * On versions of Cygwin prior to 3.0.0 there were bugs in mmap handling
- on threads (see https://trac.sagemath.org/ticket/27214#comment:25).
+ * On versions of Cygwin prior to 3.0.0 there were bugs in mmap handling
+ on threads (see :trac:`27214#comment:25`).
- * When PARI is built with multi-threading support, forking a Sage
- process from a thread leaves the main Pari interface instance broken
- (see https://trac.sagemath.org/ticket/26608#comment:38).
+ * When PARI is built with multi-threading support, forking a Sage
+ process from a thread leaves the main Pari interface instance broken
+ (see :trac:`26608#comment:38`).
In the future this may be replaced by a generalized version of the more
robust parallel processing implementation from ``sage.doctest.forker``.
@@ -133,9 +135,9 @@ def build_many(target, args, processes=None):
Processed task ...
Processed task ...
- Unlike the first version of `build_many` which was only intended to get
+ Unlike the first version of ``build_many`` which was only intended to get
around the Cygwin bug, this version can also return a result, and thus can
- be used as a replacement for `multiprocessing.Pool.map` (i.e. it still
+ be used as a replacement for ``multiprocessing.Pool.map`` (i.e. it still
blocks until the result is ready)::
sage: def square(N):
@@ -144,7 +146,7 @@ def build_many(target, args, processes=None):
[0, 1, 4, 9, ..., 9604, 9801]
If the target function raises an exception in any of the workers,
- `build_many` raises that exception and all other results are discarded.
+ ``build_many`` raises that exception and all other results are discarded.
Any in-progress tasks may still be allowed to complete gracefully before
the exception is raised::
@@ -173,7 +175,7 @@ def build_many(target, args, processes=None):
Similarly, if one of the worker processes dies unexpectedly otherwise exits
non-zero (e.g. killed by a signal) any in-progress tasks will be completed
- gracefully, but then a `RuntimeError` is raised and pending tasks are not
+ gracefully, but then a ``RuntimeError`` is raised and pending tasks are not
started::
sage: def target(N):