Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Merge #32637
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthias Koeppe committed Oct 25, 2021
2 parents 6dfadb8 + 44540f5 commit b8c3ac7
Show file tree
Hide file tree
Showing 34 changed files with 533 additions and 165 deletions.
6 changes: 3 additions & 3 deletions src/sage/databases/cremona.py
Original file line number Diff line number Diff line change
Expand Up @@ -1696,16 +1696,16 @@ def CremonaDatabase(name=None,mini=None,set_global=None):
Verify that :trac:`12341` has been resolved::
sage: c = CremonaDatabase('should not exist',mini=True)
sage: c = CremonaDatabase('should not exist', mini=True)
Traceback (most recent call last):
...
FeatureNotPresentError: Cremona's database of elliptic curves is not available.
FeatureNotPresentError: database_should_not_exist_ellcurve is not available.
'...db' not found in any of [...]
...Further installation instructions might be available at https://github.com/JohnCremona/ecdata.
sage: c = CremonaDatabase('should not exist',mini=False)
Traceback (most recent call last):
...
FeatureNotPresentError: Cremona's database of elliptic curves is not available.
FeatureNotPresentError: database_should_not_exist_ellcurve is not available.
'...db' not found in any of [...]
...Further installation instructions might be available at https://github.com/JohnCremona/ecdata.
"""
Expand Down
46 changes: 15 additions & 31 deletions src/sage/doctest/external.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@
multiprocessing.set_start_method('fork', force=True)
Array = multiprocessing.Array

import urllib.error
from urllib.request import Request, urlopen
from ssl import SSLContext

# Functions in this module whose name is of the form 'has_xxx' tests if the
# software xxx is available to Sage.
prefix = 'has_'
Expand All @@ -53,15 +49,11 @@ def has_internet():
EXAMPLES::
sage: from sage.doctest.external import has_internet
sage: has_internet() # random, optional -- internet
True
sage: has_internet() # random, optional -- internet
FeatureTestResult('internet', True)
"""
req = Request("https://www.sagemath.org",headers={"User-Agent":"sage-doctest"})
try:
urlopen(req, timeout=1, context=SSLContext())
return True
except urllib.error.URLError:
return False
from sage.features.internet import Internet
return Internet().is_present()

def has_latex():
"""
Expand Down Expand Up @@ -195,7 +187,7 @@ def has_pandoc():
sage: from sage.doctest.external import has_pandoc
sage: has_pandoc() # optional -- pandoc
FeatureTestResult('Pandoc', True)
FeatureTestResult('pandoc', True)
"""
from sage.features.pandoc import Pandoc
return Pandoc().is_present()
Expand Down Expand Up @@ -225,14 +217,10 @@ def has_cplex():
sage: from sage.doctest.external import has_cplex
sage: has_cplex() # random, optional - CPLEX
True
FeatureTestResult('cplex', True)
"""
from sage.numerical.mip import MixedIntegerLinearProgram
try:
MixedIntegerLinearProgram(solver='cplex')
return True
except Exception:
return False
from sage.features.mip_backends import CPLEX
return CPLEX().is_present()

def has_gurobi():
"""
Expand All @@ -242,14 +230,10 @@ def has_gurobi():
sage: from sage.doctest.external import has_gurobi
sage: has_gurobi() # random, optional - Gurobi
True
FeatureTestResult('gurobi', True)
"""
from sage.numerical.mip import MixedIntegerLinearProgram
try:
MixedIntegerLinearProgram(solver='gurobi')
return True
except Exception:
return False
from sage.features.mip_backends import Gurobi
return Gurobi().is_present()

def has_graphviz():
"""
Expand All @@ -259,7 +243,7 @@ def has_graphviz():
sage: from sage.doctest.external import has_graphviz
sage: has_graphviz() # optional -- graphviz
FeatureTestResult('Graphviz', True)
FeatureTestResult('graphviz', True)
"""
from sage.features.graphviz import Graphviz
return Graphviz().is_present()
Expand All @@ -272,7 +256,7 @@ def has_ffmpeg():
sage: from sage.doctest.external import has_ffmpeg
sage: has_ffmpeg() # optional -- ffmpeg
FeatureTestResult('FFmpeg', True)
FeatureTestResult('ffmpeg', True)
"""
from sage.features.ffmpeg import FFmpeg
return FFmpeg().is_present()
Expand All @@ -285,7 +269,7 @@ def has_imagemagick():
sage: from sage.doctest.external import has_imagemagick
sage: has_imagemagick() # optional -- imagemagick
FeatureTestResult('convert', True)
FeatureTestResult('imagemagick', True)
"""
from sage.features.imagemagick import ImageMagick
return ImageMagick().is_present()
Expand All @@ -299,7 +283,7 @@ def has_rubiks():
sage: from sage.doctest.external import has_rubiks
sage: has_rubiks() # optional -- rubiks
FeatureTestResult('Rubiks', True)
FeatureTestResult('rubiks', True)
"""
from sage.features.rubiks import Rubiks
return Rubiks().is_present()
Expand Down
47 changes: 33 additions & 14 deletions src/sage/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
sage: from sage.features.gap import GapPackage
sage: GapPackage("grape", spkg="gap_packages").is_present() # optional: gap_packages
FeatureTestResult('GAP package grape', True)
FeatureTestResult('gap_package_grape', True)
Note that a :class:`FeatureTestResult` acts like a bool in most contexts::
Expand Down Expand Up @@ -92,20 +92,32 @@ class Feature(TrivialUniqueRepresentation):
r"""
A feature of the runtime environment
INPUT:
- ``name`` -- (string) name of the feature; this should be suitable as an optional tag
for the Sage doctester, i.e., lowercase alphanumeric with underscores (``_``) allowed;
features that correspond to Python modules/packages may use periods (``.``)
- ``spkg`` -- (string) name of the SPKG providing the feature
- ``description`` -- (string) optional; plain English description of the feature
- ``url`` -- a URL for the upstream package providing the feature
Overwrite :meth:`_is_present` to add feature checks.
EXAMPLES::
sage: from sage.features.gap import GapPackage
sage: GapPackage("grape", spkg="gap_packages") # indirect doctest
Feature('GAP package grape')
Feature('gap_package_grape')
For efficiency, features are unique::
sage: GapPackage("grape") is GapPackage("grape")
True
"""
def __init__(self, name, spkg=None, url=None):
def __init__(self, name, spkg=None, url=None, description=None):
r"""
TESTS::
Expand All @@ -117,6 +129,8 @@ def __init__(self, name, spkg=None, url=None):
self.name = name
self.spkg = spkg
self.url = url
self.description = description

self._cache_is_present = None
self._cache_resolution = None

Expand All @@ -133,9 +147,9 @@ def is_present(self):
sage: from sage.features.gap import GapPackage
sage: GapPackage("grape", spkg="gap_packages").is_present() # optional: gap_packages
FeatureTestResult('GAP package grape', True)
FeatureTestResult('gap_package_grape', True)
sage: GapPackage("NOT_A_PACKAGE", spkg="gap_packages").is_present()
FeatureTestResult('GAP package NOT_A_PACKAGE', False)
FeatureTestResult('gap_package_NOT_A_PACKAGE', False)
The result is cached::
Expand Down Expand Up @@ -183,7 +197,7 @@ def require(self):
sage: GapPackage("ve1EeThu").require()
Traceback (most recent call last):
...
FeatureNotPresentError: GAP package ve1EeThu is not available.
FeatureNotPresentError: gap_package_ve1EeThu is not available.
`TestPackageAvailability("ve1EeThu")` evaluated to `fail` in GAP.
"""
presence = self.is_present()
Expand All @@ -198,9 +212,14 @@ def __repr__(self):
sage: from sage.features.gap import GapPackage
sage: GapPackage("grape") # indirect doctest
Feature('GAP package grape')
Feature('gap_package_grape')
sage: from sage.features.databases import DatabaseConwayPolynomials
sage: DatabaseConwayPolynomials() # indirect doctest
Feature('conway_polynomials': Frank Luebeck's database of Conway polynomials)
"""
return 'Feature({name!r})'.format(name=self.name)
description = f'{self.name!r}: {self.description}' if self.description else f'{self.name!r}'
return f'Feature({description})'

def resolution(self):
r"""
Expand Down Expand Up @@ -263,7 +282,7 @@ def __str__(self):
sage: GapPackage("gapZuHoh8Uu").require() # indirect doctest
Traceback (most recent call last):
...
FeatureNotPresentError: GAP package gapZuHoh8Uu is not available.
FeatureNotPresentError: gap_package_gapZuHoh8Uu is not available.
`TestPackageAvailability("gapZuHoh8Uu")` evaluated to `fail` in GAP.
"""
lines = ["{feature} is not available.".format(feature=self.feature.name)]
Expand All @@ -285,7 +304,7 @@ class FeatureTestResult(object):
sage: from sage.features.gap import GapPackage
sage: presence = GapPackage("NOT_A_PACKAGE").is_present(); presence # indirect doctest
FeatureTestResult('GAP package NOT_A_PACKAGE', False)
FeatureTestResult('gap_package_NOT_A_PACKAGE', False)
sage: bool(presence)
False
Expand All @@ -304,9 +323,9 @@ class FeatureTestResult(object):
sage: from sage.features import FeatureTestResult
sage: package = GapPackage("NOT_A_PACKAGE", spkg="no_package")
sage: str(FeatureTestResult(package, True).resolution) # optional - sage_spkg
'...To install GAP package NOT_A_PACKAGE...you can try to run...sage -i no_package...'
'...To install gap_package_NOT_A_PACKAGE...you can try to run...sage -i no_package...'
sage: str(FeatureTestResult(package, False).resolution) # optional - sage_spkg
'...To install GAP package NOT_A_PACKAGE...you can try to run...sage -i no_package...'
'...To install gap_package_NOT_A_PACKAGE...you can try to run...sage -i no_package...'
sage: FeatureTestResult(package, False, resolution="rtm").resolution
'rtm'
"""
Expand Down Expand Up @@ -801,8 +820,8 @@ def __init__(self, name, **kwds):
TESTS::
sage: from sage.features import PythonModule
sage: from sage.features.fes import LibFES
sage: isinstance(LibFES(), PythonModule) # indirect doctest
sage: from sage.features.databases import DatabaseKnotInfo
sage: isinstance(DatabaseKnotInfo(), PythonModule) # indirect doctest
True
"""
Feature.__init__(self, name, **kwds)
Expand Down
6 changes: 3 additions & 3 deletions src/sage/features/csdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class CSDP(Executable):
sage: from sage.features.csdp import CSDP
sage: CSDP().is_present() # optional: csdp
FeatureTestResult('CSDP', True)
FeatureTestResult('csdp', True)
"""
def __init__(self):
r"""
Expand All @@ -30,7 +30,7 @@ def __init__(self):
sage: isinstance(CSDP(), CSDP)
True
"""
Executable.__init__(self, name="CSDP", spkg="csdp", executable="theta",
Executable.__init__(self, name="csdp", spkg="csdp", executable="theta",
url="https://github.com/dimpase/csdp")

def is_functional(self):
Expand All @@ -41,7 +41,7 @@ def is_functional(self):
sage: from sage.features.csdp import CSDP
sage: CSDP().is_functional() # optional: csdp
FeatureTestResult('CSDP', True)
FeatureTestResult('csdp', True)
"""
from sage.misc.temporary_file import tmp_filename
tf_name = tmp_filename()
Expand Down
21 changes: 12 additions & 9 deletions src/sage/features/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DatabaseConwayPolynomials(StaticFile):
sage: from sage.features.databases import DatabaseConwayPolynomials
sage: DatabaseConwayPolynomials().is_present()
FeatureTestResult("Frank Luebeck's database of Conway polynomials", True)
FeatureTestResult('conway_polynomials', True)
"""

def __init__(self):
Expand All @@ -34,10 +34,11 @@ def __init__(self):
search_path = [CONWAY_POLYNOMIALS_DATA_DIR]
else:
search_path = []
StaticFile.__init__(self, "Frank Luebeck's database of Conway polynomials",
StaticFile.__init__(self, "conway_polynomials",
filename='conway_polynomials.p',
search_path=search_path,
spkg='conway_polynomials')
spkg='conway_polynomials',
description="Frank Luebeck's database of Conway polynomials")


CREMONA_DATA_DIRS = set([CREMONA_MINI_DATA_DIR, CREMONA_LARGE_DATA_DIR])
Expand All @@ -57,9 +58,9 @@ class DatabaseCremona(StaticFile):
sage: from sage.features.databases import DatabaseCremona
sage: DatabaseCremona('cremona_mini').is_present()
FeatureTestResult("Cremona's database of elliptic curves", True)
FeatureTestResult('database_cremona_mini_ellcurve', True)
sage: DatabaseCremona().is_present() # optional: database_cremona_ellcurve
FeatureTestResult("Cremona's database of elliptic curves", True)
FeatureTestResult('database_cremona_ellcurve', True)
"""
def __init__(self, name="cremona", spkg="database_cremona_ellcurve"):
r"""
Expand All @@ -69,11 +70,12 @@ def __init__(self, name="cremona", spkg="database_cremona_ellcurve"):
sage: isinstance(DatabaseCremona(), DatabaseCremona)
True
"""
StaticFile.__init__(self, "Cremona's database of elliptic curves",
StaticFile.__init__(self, f"database_{name}_ellcurve",
filename='{}.db'.format(name.replace(' ', '_')),
search_path=CREMONA_DATA_DIRS,
spkg=spkg,
url="https://github.com/JohnCremona/ecdata")
url="https://github.com/JohnCremona/ecdata",
description="Cremona's database of elliptic curves")


class DatabaseJones(StaticFile):
Expand All @@ -94,9 +96,10 @@ def __init__(self):
sage: isinstance(DatabaseJones(), DatabaseJones)
True
"""
StaticFile.__init__(self, "John Jones's tables of number fields",
StaticFile.__init__(self, "database_jones_numfield",
filename='jones/jones.sobj',
spkg="database_jones_numfield")
spkg="database_jones_numfield",
description="John Jones's tables of number fields")


class DatabaseKnotInfo(PythonModule):
Expand Down
10 changes: 7 additions & 3 deletions src/sage/features/fes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"""

from . import CythonFeature, PythonModule
from .join_feature import JoinFeature


TEST_CODE = """
# distutils: libraries=fes
Expand Down Expand Up @@ -70,7 +72,7 @@ def __init__(self):
url="http://www.lifl.fr/~bouillag/fes/")


class LibFES(PythonModule):
class LibFES(JoinFeature):
r"""
A :class:`Feature` which describes whether the :mod:`sage.libs.fes`
module has been enabled for this build of Sage and is functional.
Expand All @@ -88,5 +90,7 @@ def __init__(self):
sage: isinstance(LibFES(), LibFES)
True
"""
PythonModule.__init__(self, "sage.libs.fes", spkg="fes",
url="http://www.lifl.fr/~bouillag/fes/")
JoinFeature.__init__(self, 'fes',
[PythonModule("sage.libs.fes")],
spkg="fes",
url="http://www.lifl.fr/~bouillag/fes/")
4 changes: 2 additions & 2 deletions src/sage/features/ffmpeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class FFmpeg(Executable):
sage: from sage.features.ffmpeg import FFmpeg
sage: FFmpeg().is_present() # optional: ffmpeg
FeatureTestResult('FFmpeg', True)
FeatureTestResult('ffmpeg', True)
"""
def __init__(self):
r"""
Expand All @@ -33,5 +33,5 @@ def __init__(self):
sage: isinstance(FFmpeg(), FFmpeg)
True
"""
Executable.__init__(self, "FFmpeg", executable="ffmpeg",
Executable.__init__(self, "ffmpeg", executable="ffmpeg",
url="https://www.ffmpeg.org/")
Loading

0 comments on commit b8c3ac7

Please sign in to comment.