Features for testing the presence of Python modules in the Sage library

All of these features are present in a monolithic installation of the Sage library, such as the one made by the SageMath distribution.

The features are defined for the purpose of separately testing modularized distributions such as sagemath-categories and sagemath-repl.

Often, doctests in a module of the Sage library illustrate the interplay with a range of different objects; this is a form of integration testing. These objects may come from modules shipped in other distributions. For example, sage.structure.element (shipped by sagemath-objects, one of the most fundamental distributions) contains the doctest:

sage: G = SymmetricGroup(4)                                                         # needs sage.groups
sage: g = G([2, 3, 4, 1])                                                           # needs sage.groups
sage: g.powers(4)                                                                   # needs sage.groups
[(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)]
>>> from sage.all import *
>>> G = SymmetricGroup(Integer(4))                                                         # needs sage.groups
>>> g = G([Integer(2), Integer(3), Integer(4), Integer(1)])                                                           # needs sage.groups
>>> g.powers(Integer(4))                                                                   # needs sage.groups
[(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)]

This test cannot pass when the distribution sagemath-objects is tested separately (in a virtual environment): In this situation, SymmetricGroup is not defined anywhere (and thus not present in the top-level namespace). Hence, we conditionalize this doctest on the presence of the feature sage.groups.

class sage.features.sagemath.SAGE_SRC(*args, **kwds)[source]

Bases: StaticFile

A Feature which describes the presence of the monolithic source tree of the Sage library.

sage.features.sagemath.all_features()[source]

Return features corresponding to parts of the Sage library.

These features are named after Python packages/modules (e.g., sage.symbolic), not distribution packages (sagemath-symbolics).

This design is motivated by a separation of concerns: The author of a module that depends on some functionality provided by a Python module usually already knows the name of the Python module, so we do not want to force the author to also know about the distribution package that provides the Python module.

Instead, we associate distribution packages to Python modules in sage.features.sagemath via the spkg parameter of Feature.

EXAMPLES:

sage: from sage.features.sagemath import all_features
sage: list(all_features())
[...Feature('sage.combinat'), ...]
>>> from sage.all import *
>>> from sage.features.sagemath import all_features
>>> list(all_features())
[...Feature('sage.combinat'), ...]
class sage.features.sagemath.sage__combinat(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.combinat.

EXAMPLES:

Python modules that provide elementary combinatorial objects such as sage.combinat.subset, sage.combinat.composition, sage.combinat.permutation are always available; there is no need for an # optional/needs tag:

sage: Permutation([1,2,3]).is_even()
True
sage: Permutation([6,1,4,5,2,3]).bruhat_inversions()
[[0, 1], [0, 2], [0, 3], [2, 4], [2, 5], [3, 4], [3, 5]]
>>> from sage.all import *
>>> Permutation([Integer(1),Integer(2),Integer(3)]).is_even()
True
>>> Permutation([Integer(6),Integer(1),Integer(4),Integer(5),Integer(2),Integer(3)]).bruhat_inversions()
[[0, 1], [0, 2], [0, 3], [2, 4], [2, 5], [3, 4], [3, 5]]

Use # needs sage.combinat for doctests that use any other Python modules from sage.combinat, for example sage.combinat.tableau_tuple:

sage: TableauTuple([[[7,8,9]],[],[[1,2,3],[4,5],[6]]]).shape()                  # needs sage.combinat
([3], [], [3, 2, 1])
>>> from sage.all import *
>>> TableauTuple([[[Integer(7),Integer(8),Integer(9)]],[],[[Integer(1),Integer(2),Integer(3)],[Integer(4),Integer(5)],[Integer(6)]]]).shape()                  # needs sage.combinat
([3], [], [3, 2, 1])

Doctests that use Python modules from sage.combinat that involve trees, graphs, hypergraphs, posets, quivers, combinatorial designs, finite state machines etc. should be marked # needs sage.combinat sage.graphs:

sage: L = Poset({0: [1], 1: [2], 2:[3], 3:[4]})                                 # needs sage.combinat sage.graphs
sage: L.is_chain()                                                              # needs sage.combinat sage.graphs
True
>>> from sage.all import *
>>> L = Poset({Integer(0): [Integer(1)], Integer(1): [Integer(2)], Integer(2):[Integer(3)], Integer(3):[Integer(4)]})                                 # needs sage.combinat sage.graphs
>>> L.is_chain()                                                              # needs sage.combinat sage.graphs
True

Doctests that use combinatorial modules/algebras, or root systems should use the tag # needs sage.combinat sage.modules:

sage: # needs sage.combinat sage.modules
sage: A = SchurAlgebra(QQ, 2, 3)
sage: a = A.an_element(); a
2*S((1, 1, 1), (1, 1, 1)) + 2*S((1, 1, 1), (1, 1, 2))
 + 3*S((1, 1, 1), (1, 2, 2))
sage: L = RootSystem(['A',3,1]).root_lattice()
sage: PIR = L.positive_imaginary_roots(); PIR
Positive imaginary roots of type ['A', 3, 1]
>>> from sage.all import *
>>> # needs sage.combinat sage.modules
>>> A = SchurAlgebra(QQ, Integer(2), Integer(3))
>>> a = A.an_element(); a
2*S((1, 1, 1), (1, 1, 1)) + 2*S((1, 1, 1), (1, 1, 2))
 + 3*S((1, 1, 1), (1, 2, 2))
>>> L = RootSystem(['A',Integer(3),Integer(1)]).root_lattice()
>>> PIR = L.positive_imaginary_roots(); PIR
Positive imaginary roots of type ['A', 3, 1]

Doctests that use lattices, semilattices, or Dynkin diagrams should use the tag # needs sage.combinat sage.graphs sage.modules:

sage: L = LatticePoset({0: [1,2], 1: [3], 2: [3,4], 3: [5], 4: [5]})            # needs sage.combinat sage.graphs sage.modules
sage: L.meet_irreducibles()                                                     # needs sage.combinat sage.graphs sage.modules
[1, 3, 4]
>>> from sage.all import *
>>> L = LatticePoset({Integer(0): [Integer(1),Integer(2)], Integer(1): [Integer(3)], Integer(2): [Integer(3),Integer(4)], Integer(3): [Integer(5)], Integer(4): [Integer(5)]})            # needs sage.combinat sage.graphs sage.modules
>>> L.meet_irreducibles()                                                     # needs sage.combinat sage.graphs sage.modules
[1, 3, 4]
class sage.features.sagemath.sage__geometry__polyhedron(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.geometry.polyhedron.

EXAMPLES:

Doctests that use polyhedra, cones, geometric complexes, triangulations, etc. should use the tag # needs sage.geometry.polyhedron:

sage: co = polytopes.truncated_tetrahedron()                                    # needs sage.geometry.polyhedron
sage: co.volume()                                                               # needs sage.geometry.polyhedron
184/3
>>> from sage.all import *
>>> co = polytopes.truncated_tetrahedron()                                    # needs sage.geometry.polyhedron
>>> co.volume()                                                               # needs sage.geometry.polyhedron
184/3

Some constructions of polyhedra require additional tags:

sage: # needs sage.combinat sage.geometry.polyhedron sage.rings.number_field
sage: perm_a3_reg_nf = polytopes.generalized_permutahedron(
....:    ['A',3], regular=True, backend='number_field'); perm_a3_reg_nf
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
>>> from sage.all import *
>>> # needs sage.combinat sage.geometry.polyhedron sage.rings.number_field
>>> perm_a3_reg_nf = polytopes.generalized_permutahedron(
...    ['A',Integer(3)], regular=True, backend='number_field'); perm_a3_reg_nf
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
class sage.features.sagemath.sage__graphs(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.graphs.

EXAMPLES:

Doctests that use anything from sage.graphs (Graph, DiGraph, …) should be marked # needs sage.graphs. The same applies to any doctest that uses a Poset, cluster algebra quiver, finite state machines, abelian sandpiles, or Dynkin diagrams:

sage: g = graphs.PetersenGraph()                                                # needs sage.graphs
sage: r, s = g.is_weakly_chordal(certificate=True); r                           # needs sage.graphs
False
>>> from sage.all import *
>>> g = graphs.PetersenGraph()                                                # needs sage.graphs
>>> r, s = g.is_weakly_chordal(certificate=True); r                           # needs sage.graphs
False

Also any use of tree classes defined in sage.combinat (BinaryTree, RootedTree, …) in doctests should be marked the same.

By way of generalization, any use of SimplicialComplex or other abstract complexes from sage.topology, hypergraphs, and combinatorial designs, should be marked # needs sage.graphs as well:

sage: X = SimplicialComplex([[0,1,2], [1,2,3]])                                 # needs sage.graphs
sage: X.link(Simplex([0]))                                                      # needs sage.graphs
Simplicial complex with vertex set (1, 2) and facets {(1, 2)}

sage: IncidenceStructure([[1,2,3],[1,4]]).degrees(2)                            # needs sage.graphs
{(1, 2): 1, (1, 3): 1, (1, 4): 1, (2, 3): 1, (2, 4): 0, (3, 4): 0}
>>> from sage.all import *
>>> X = SimplicialComplex([[Integer(0),Integer(1),Integer(2)], [Integer(1),Integer(2),Integer(3)]])                                 # needs sage.graphs
>>> X.link(Simplex([Integer(0)]))                                                      # needs sage.graphs
Simplicial complex with vertex set (1, 2) and facets {(1, 2)}

>>> IncidenceStructure([[Integer(1),Integer(2),Integer(3)],[Integer(1),Integer(4)]]).degrees(Integer(2))                            # needs sage.graphs
{(1, 2): 1, (1, 3): 1, (1, 4): 1, (2, 3): 1, (2, 4): 0, (3, 4): 0}

On the other hand, matroids are not implemented as posets in Sage but are instead closely tied to linear algebra over fields; hence use # needs sage.modules instead:

sage: # needs sage.modules
sage: M = Matroid(Matrix(QQ, [[1, 0, 0, 0, 1, 1, 1],
....:                         [0, 1, 0, 1, 0, 1, 1],
....:                         [0, 0, 1, 1, 1, 0, 1]]))
sage: N = (M / [2]).delete([3, 4])
sage: sorted(N.groundset())
[0, 1, 5, 6]
>>> from sage.all import *
>>> # needs sage.modules
>>> M = Matroid(Matrix(QQ, [[Integer(1), Integer(0), Integer(0), Integer(0), Integer(1), Integer(1), Integer(1)],
...                         [Integer(0), Integer(1), Integer(0), Integer(1), Integer(0), Integer(1), Integer(1)],
...                         [Integer(0), Integer(0), Integer(1), Integer(1), Integer(1), Integer(0), Integer(1)]]))
>>> N = (M / [Integer(2)]).delete([Integer(3), Integer(4)])
>>> sorted(N.groundset())
[0, 1, 5, 6]

However, many constructions (and some methods) of matroids do involve graphs:

sage: # needs sage.modules
sage: W = matroids.Wheel(3)     # despite the name, not created via graphs
sage: W.is_isomorphic(N)           # goes through a graph isomorphism test      # needs sage.graphs
False
sage: K4 = matroids.CompleteGraphic(4)    # this one is created via graphs      # needs sage.graphs
sage: K4.is_isomorphic(W)                                                       # needs sage.graphs
True
>>> from sage.all import *
>>> # needs sage.modules
>>> W = matroids.Wheel(Integer(3))     # despite the name, not created via graphs
>>> W.is_isomorphic(N)           # goes through a graph isomorphism test      # needs sage.graphs
False
>>> K4 = matroids.CompleteGraphic(Integer(4))    # this one is created via graphs      # needs sage.graphs
>>> K4.is_isomorphic(W)                                                       # needs sage.graphs
True
class sage.features.sagemath.sage__groups(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.groups.

EXAMPLES:

Permutations and sets of permutations are always available, but permutation groups are implemented in Sage using the GAP system and require the tag # needs sage.groups:

sage: p = Permutation([2,1,4,3])
sage: p.to_permutation_group_element()                                          # needs sage.groups
(1,2)(3,4)
>>> from sage.all import *
>>> p = Permutation([Integer(2),Integer(1),Integer(4),Integer(3)])
>>> p.to_permutation_group_element()                                          # needs sage.groups
(1,2)(3,4)
class sage.features.sagemath.sage__libs__braiding(*args, **kwds)[source]

Bases: PythonModule

A Feature describing the presence of sage.libs.braiding.

EXAMPLES:

sage: from sage.features.sagemath import sage__libs__braiding
sage: sage__libs__braiding().is_present()                                            # needs sage.libs.braiding
FeatureTestResult('sage.libs.braiding', True)
>>> from sage.all import *
>>> from sage.features.sagemath import sage__libs__braiding
>>> sage__libs__braiding().is_present()                                            # needs sage.libs.braiding
FeatureTestResult('sage.libs.braiding', True)
class sage.features.sagemath.sage__libs__ecl(*args, **kwds)[source]

Bases: PythonModule

A Feature describing the presence of sage.libs.ecl.

EXAMPLES:

sage: from sage.features.sagemath import sage__libs__ecl
sage: sage__libs__ecl().is_present()                        # optional - sage.libs.ecl
FeatureTestResult('sage.libs.ecl', True)
>>> from sage.all import *
>>> from sage.features.sagemath import sage__libs__ecl
>>> sage__libs__ecl().is_present()                        # optional - sage.libs.ecl
FeatureTestResult('sage.libs.ecl', True)
class sage.features.sagemath.sage__libs__flint(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.flint and other modules depending on FLINT.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

class sage.features.sagemath.sage__libs__gap(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.gap (the library interface to GAP) and sage.interfaces.gap (the pexpect interface to GAP). By design, we do not distinguish between these two, in order to facilitate the conversion of code from the pexpect interface to the library interface.

See also

Features for GAP packages

class sage.features.sagemath.sage__libs__giac(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.giac.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

class sage.features.sagemath.sage__libs__homfly(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.homfly.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

class sage.features.sagemath.sage__libs__linbox(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.linbox and other modules depending on Givaro, FFLAS-FFPACK, LinBox.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

class sage.features.sagemath.sage__libs__m4ri(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of Cython modules depending on the M4RI and/or M4RIe libraries.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

class sage.features.sagemath.sage__libs__ntl(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.ntl and other modules depending on NTL.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

class sage.features.sagemath.sage__libs__pari(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.libs.pari.

SageMath uses the PARI library (via cypari2) for numerous purposes. Doctests that involves such features should be marked # needs sage.libs.pari.

In addition to the modularization purposes that this tag serves, it also provides attribution to the upstream project.

EXAMPLES:

sage: R.<a> = QQ[]
sage: S.<x> = R[]
sage: f = x^2 + a; g = x^3 + a
sage: r = f.resultant(g); r                                                     # needs sage.libs.pari
a^3 + a^2
>>> from sage.all import *
>>> R = QQ['a']; (a,) = R._first_ngens(1)
>>> S = R['x']; (x,) = S._first_ngens(1)
>>> f = x**Integer(2) + a; g = x**Integer(3) + a
>>> r = f.resultant(g); r                                                     # needs sage.libs.pari
a^3 + a^2
class sage.features.sagemath.sage__libs__singular(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.libs.singular (the library interface to Singular) and sage.interfaces.singular (the pexpect interface to Singular). By design, we do not distinguish between these two, in order to facilitate the conversion of code from the pexpect interface to the library interface.

See also

Feature singular

class sage.features.sagemath.sage__modular(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.modular.

class sage.features.sagemath.sage__modules(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.modules.

EXAMPLES:

All uses of implementations of vector spaces / free modules in SageMath, whether sage.modules.free_module.FreeModule, sage.combinat.free_module.CombinatorialFreeModule, sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule, or additive abelian groups, should be marked # needs sage.modules.

The same holds for matrices, tensors, algebras, quadratic forms, point lattices, root systems, matrix/affine/Weyl/Coxeter groups, matroids, and ring derivations.

Likewise, all uses of sage.coding, sage.crypto, and sage.homology in doctests should be marked # needs sage.modules.

class sage.features.sagemath.sage__numerical__mip(*args, **kwds)[source]

Bases: PythonModule

A Feature describing the presence of sage.numerical.mip.

class sage.features.sagemath.sage__plot(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.plot.

class sage.features.sagemath.sage__rings__complex_double(*args, **kwds)[source]

Bases: PythonModule

A Feature describing the presence of sage.rings.complex_double.

class sage.features.sagemath.sage__rings__finite_rings(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.rings.finite_rings; specifically, the element implementations using the PARI library.

class sage.features.sagemath.sage__rings__function_field(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.rings.function_field.

EXAMPLES:

Rational function fields are always available:

sage: K.<x> = FunctionField(QQ)
sage: K.maximal_order()
Maximal order of Rational function field in x over Rational Field
>>> from sage.all import *
>>> K = FunctionField(QQ, names=('x',)); (x,) = K._first_ngens(1)
>>> K.maximal_order()
Maximal order of Rational function field in x over Rational Field

Use the tag # needs sage.rings.function_field whenever extensions of function fields (by adjoining a root of a univariate polynomial) come into play:

sage: R.<y> = K[]
sage: L.<y> = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L                         # needs sage.rings.function_field
Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x
>>> from sage.all import *
>>> R = K['y']; (y,) = R._first_ngens(1)
>>> L = K.extension(y**Integer(5) - (x**Integer(3) + Integer(2)*x*y + Integer(1)/x), names=('y',)); (y,) = L._first_ngens(1); L                         # needs sage.rings.function_field
Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x

Such extensions of function fields are implemented using Gröbner bases of polynomial rings; Sage makes essential use of the Singular system for this. (It is not necessary to use the tag # needs sage.libs.singular; it is implied by # needs sage.rings.function_field.)

class sage.features.sagemath.sage__rings__number_field(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.rings.number_field.

Number fields are implemented in Sage using a complicated mixture of various libraries, including FLINT, GAP, MPFI, NTL, and PARI.

EXAMPLES:

Rational numbers are, of course, always available:

sage: QQ in NumberFields()
True
>>> from sage.all import *
>>> QQ in NumberFields()
True

Doctests that construct algebraic number fields should be marked # needs sage.rings.number_field:

sage: # needs sage.rings.number_field
sage: K.<cuberoot2> = NumberField(x^3 - 2)
sage: L.<cuberoot3> = K.extension(x^3 - 3)
sage: S.<sqrt2> = L.extension(x^2 - 2); S
Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field

sage: # needs sage.rings.number_field
sage: K.<zeta> = CyclotomicField(15)
sage: CC(zeta)
0.913545457642601 + 0.406736643075800*I
>>> from sage.all import *
>>> # needs sage.rings.number_field
>>> K = NumberField(x**Integer(3) - Integer(2), names=('cuberoot2',)); (cuberoot2,) = K._first_ngens(1)
>>> L = K.extension(x**Integer(3) - Integer(3), names=('cuberoot3',)); (cuberoot3,) = L._first_ngens(1)
>>> S = L.extension(x**Integer(2) - Integer(2), names=('sqrt2',)); (sqrt2,) = S._first_ngens(1); S
Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field

>>> # needs sage.rings.number_field
>>> K = CyclotomicField(Integer(15), names=('zeta',)); (zeta,) = K._first_ngens(1)
>>> CC(zeta)
0.913545457642601 + 0.406736643075800*I

Doctests that make use of the algebraic field QQbar or the algebraic real field AA should be marked likewise:

sage: # needs sage.rings.number_field
sage: AA(-1)^(1/3)
-1
sage: QQbar(-1)^(1/3)
0.500000000000000? + 0.866025403784439?*I
>>> from sage.all import *
>>> # needs sage.rings.number_field
>>> AA(-Integer(1))**(Integer(1)/Integer(3))
-1
>>> QQbar(-Integer(1))**(Integer(1)/Integer(3))
0.500000000000000? + 0.866025403784439?*I

Use of the universal cyclotomic field should be marked # needs sage.libs.gap sage.rings.number_field.

sage: # needs sage.libs.gap sage.rings.number_field sage: UCF = UniversalCyclotomicField(); UCF Universal Cyclotomic Field sage: E = UCF.gen sage: f = E(2) + E(3); f 2*E(3) + E(3)^2 sage: f.galois_conjugates() [2*E(3) + E(3)^2, E(3) + 2*E(3)^2]

class sage.features.sagemath.sage__rings__padics(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.rings.padics.

class sage.features.sagemath.sage__rings__polynomial__pbori(*args, **kwds)[source]

Bases: JoinFeature

A sage.features.Feature describing the presence of sage.rings.polynomial.pbori.

class sage.features.sagemath.sage__rings__real_double(*args, **kwds)[source]

Bases: PythonModule

A Feature describing the presence of sage.rings.real_double.

EXAMPLES:

The Real Double Field is basically always available, and no # optional/needs tag is needed:

sage: RDF.characteristic()
0
>>> from sage.all import *
>>> RDF.characteristic()
0

The feature exists for use in doctests of Python modules that are shipped by the most fundamental distributions.

class sage.features.sagemath.sage__rings__real_mpfr(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.rings.real_mpfr.

class sage.features.sagemath.sage__sat(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.sat.

class sage.features.sagemath.sage__schemes(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.schemes.

class sage.features.sagemath.sage__symbolic(*args, **kwds)[source]

Bases: JoinFeature

A Feature describing the presence of sage.symbolic.

EXAMPLES:

The symbolics subsystem of Sage will be provided by the distribution sagemath-symbolics, in preparation at Issue #35095. If it is not installed, Sage will be able to provide installation advice:

sage: from sage.features.sagemath import sage__symbolic
sage: print(sage__symbolic().resolution())                                      # optional - sage_spkg, not tested
...To install sagemath_symbolics...you can try to run...
pip install sagemath-symbolics
...
>>> from sage.all import *
>>> from sage.features.sagemath import sage__symbolic
>>> print(sage__symbolic().resolution())                                      # optional - sage_spkg, not tested
...To install sagemath_symbolics...you can try to run...
pip install sagemath-symbolics
...
class sage.features.sagemath.sagemath_doc_html(*args, **kwds)[source]

Bases: StaticFile

A Feature which describes the presence of the documentation of the Sage library in HTML format.

Developers often use make build instead of make to avoid the long time it takes to compile the documentation. Although commands such as make ptest build the documentation before testing, other test commands such as make ptestlong-nodoc or ./sage -t --all do not.

All doctests that refer to the built documentation need to be marked # needs sagemath_doc_html.