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

Commit

Permalink
Merge #32925
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthias Koeppe committed Dec 15, 2021
2 parents b65afba + 547206c commit d1d603d
Show file tree
Hide file tree
Showing 36 changed files with 400 additions and 334 deletions.
2 changes: 1 addition & 1 deletion src/doc/en/developer/coding_basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@ framework. Here is a comprehensive list:
The following should yield 4. See :trac:`2`. ::
sage: 2+2 # optional: bug
sage: 2+2 # optional - bug
5
sage: 2+2 # known bug
5
Expand Down
48 changes: 40 additions & 8 deletions src/doc/en/reference/misc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,45 @@ Fast Expression Evaluation
.. sage/ext/interpreters/wrapper_rdf
.. sage/ext/interpreters/wrapper_rr
Features
~~~~~~~~

.. toctree::
:maxdepth: 1

sage/features
sage/features/join_feature
sage/features/all
sage/features/sagemath
sage/features/pkg_systems
sage/features/bliss
sage/features/csdp
sage/features/databases
sage/features/dvipng
sage/features/fes
sage/features/ffmpeg
sage/features/four_ti_2
sage/features/gap
sage/features/graph_generators
sage/features/graphviz
sage/features/imagemagick
sage/features/interfaces
sage/features/internet
sage/features/kenzo
sage/features/latex
sage/features/latte
sage/features/lrs
sage/features/mcqd
sage/features/meataxe
sage/features/mip_backends
sage/features/normaliz
sage/features/pandoc
sage/features/pdf2svg
sage/features/polymake
sage/features/rubiks
sage/features/tdlib


Code Evaluation
---------------

Expand Down Expand Up @@ -229,14 +268,7 @@ Distribution

sage/misc/package
sage/misc/dist
sage/features
sage/features/bliss
sage/features/csdp
sage/features/databases
sage/features/fes
sage/features/gap
sage/features/graph_generators
sage/features/lrs


Credits
~~~~~~~
Expand Down
6 changes: 3 additions & 3 deletions src/sage/combinat/posets/posets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6232,9 +6232,9 @@ def canonical_label(self, algorithm=None):
sage: Poset().canonical_label() # Test the empty poset
Finite poset containing 0 elements
sage: D2 = posets.DiamondPoset(4).canonical_label(algorithm='bliss') # optional: bliss
sage: B2 = posets.BooleanLattice(2).canonical_label(algorithm='bliss') # optional: bliss
sage: D2 == B2 # optional: bliss
sage: D2 = posets.DiamondPoset(4).canonical_label(algorithm='bliss') # optional - bliss
sage: B2 = posets.BooleanLattice(2).canonical_label(algorithm='bliss') # optional - bliss
sage: D2 == B2 # optional - bliss
True
"""
canonical_label = self._hasse_diagram.canonical_label(certificate=True,
Expand Down
188 changes: 12 additions & 176 deletions src/sage/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
Testing for features of the environment at runtime
A computation can require a certain package to be installed in the runtime
environment. Abstractly such a package describes a :class`Feature` which can
environment. Abstractly such a package describes a :class:`Feature` which can
be tested for at runtime. It can be of various kinds, most prominently an
:class:`Executable` in the PATH or an additional package for some installed
system such as a :class:`GapPackage`.
:class:`Executable` in the ``PATH``, a :class:`PythonModule`, or an additional
package for some installed
system such as a :class:`~sage.features.gap.GapPackage`.
AUTHORS:
Expand All @@ -27,7 +28,7 @@
Here we test whether the grape GAP package is available::
sage: from sage.features.gap import GapPackage
sage: GapPackage("grape", spkg="gap_packages").is_present() # optional: gap_packages
sage: GapPackage("grape", spkg="gap_packages").is_present() # optional - gap_packages
FeatureTestResult('gap_package_grape', True)
Note that a :class:`FeatureTestResult` acts like a bool in most contexts::
Expand Down Expand Up @@ -59,7 +60,7 @@

class TrivialClasscallMetaClass(type):
"""
A trivial version of :class:`ClasscallMetaclass` without Cython dependencies.
A trivial version of :class:`sage.misc.classcall_metaclass.ClasscallMetaclass` without Cython dependencies.
"""
def __call__(cls, *args, **kwds):
r"""
Expand Down Expand Up @@ -146,7 +147,7 @@ def is_present(self):
EXAMPLES::
sage: from sage.features.gap import GapPackage
sage: GapPackage("grape", spkg="gap_packages").is_present() # optional: gap_packages
sage: GapPackage("grape", spkg="gap_packages").is_present() # optional - gap_packages
FeatureTestResult('gap_package_grape', True)
sage: GapPackage("NOT_A_PACKAGE", spkg="gap_packages").is_present()
FeatureTestResult('gap_package_NOT_A_PACKAGE', False)
Expand Down Expand Up @@ -251,6 +252,7 @@ def find_resolution():

return lazy_string(find_resolution)


class FeatureNotPresentError(RuntimeError):
r"""
A missing feature error.
Expand Down Expand Up @@ -373,7 +375,8 @@ def __repr__(self):

def package_systems():
"""
Return a list of ``PackageSystem`` objects representing the available package systems.
Return a list of :class:~sage.features.pkg_systems.PackageSystem` objects
representing the available package systems.
The list is ordered by decreasing preference.
Expand All @@ -387,6 +390,7 @@ def package_systems():
from subprocess import run, CalledProcessError, PIPE
global _cache_package_systems
if _cache_package_systems is None:
from .pkg_systems import PackageSystem, SagePackageSystem, PipPackageSystem
_cache_package_systems = []
# Try to use scripts from SAGE_ROOT (or an installation of sage_bootstrap)
# to obtain system package advice.
Expand All @@ -402,178 +406,10 @@ def package_systems():

return _cache_package_systems

class PackageSystem(Feature):
r"""
A feature describing a system package manager.
EXAMPLES::
sage: from sage.features import PackageSystem
sage: PackageSystem('conda')
Feature('conda')
"""
def _is_present(self):
r"""
Test whether ``self`` appears in the list of available package systems.
EXAMPLES::
sage: from sage.features import PackageSystem
sage: debian = PackageSystem('debian')
sage: debian.is_present() # indirect doctest, random
True
"""
return self in package_systems()

def spkg_installation_hint(self, spkgs, *, prompt=" !", feature=None):
r"""
Return a string that explains how to install ``feature``.
EXAMPLES::
sage: from sage.features import PackageSystem
sage: homebrew = PackageSystem('homebrew')
sage: homebrew.spkg_installation_hint('openblas') # optional - SAGE_ROOT
'To install openblas using the homebrew package manager, you can try to run:\n!brew install openblas'
"""
if isinstance(spkgs, (tuple, list)):
spkgs = ' '.join(spkgs)
if feature is None:
feature = spkgs
return self._spkg_installation_hint(spkgs, prompt, feature)

def _spkg_installation_hint(self, spkgs, prompt, feature):
r"""
Return a string that explains how to install ``feature``.
Override this method in derived classes.
EXAMPLES::
sage: from sage.features import PackageSystem
sage: fedora = PackageSystem('fedora')
sage: fedora.spkg_installation_hint('openblas') # optional - SAGE_ROOT
'To install openblas using the fedora package manager, you can try to run:\n!sudo yum install openblas-devel'
"""
from subprocess import run, CalledProcessError, PIPE
lines = []
system = self.name
try:
proc = run(f'sage-get-system-packages {system} {spkgs}',
shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True, check=True)
system_packages = proc.stdout.strip()
print_sys = f'sage-print-system-package-command {system} --verbose --sudo --prompt="{prompt}"'
command = f'{print_sys} update && {print_sys} install {system_packages}'
proc = run(command, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True, check=True)
command = proc.stdout.strip()
if command:
lines.append(f'To install {feature} using the {system} package manager, you can try to run:')
lines.append(command)
return '\n'.join(lines)
except CalledProcessError:
pass
return f'No equivalent system packages for {system} are known to Sage.'

class SagePackageSystem(PackageSystem):
r"""
The feature describing the Sage package manager.
EXAMPLES::
sage: from sage.features import SagePackageSystem
sage: SagePackageSystem()
Feature('sage_spkg')
"""
@staticmethod
def __classcall__(cls):
r"""
Normalize initargs.
TESTS::
sage: from sage.features import SagePackageSystem
sage: SagePackageSystem() is SagePackageSystem() # indirect doctest
True
"""
return PackageSystem.__classcall__(cls, "sage_spkg")

def _is_present(self):
r"""
Test whether ``sage-spkg`` is available.
EXAMPLES::
sage: from sage.features import SagePackageSystem
sage: bool(SagePackageSystem().is_present()) # indirect doctest, optional - sage_spkg
True
"""
from subprocess import run, DEVNULL, CalledProcessError
try:
# "sage -p" is a fast way of checking whether sage-spkg is available.
run('sage -p', shell=True, stdout=DEVNULL, stderr=DEVNULL, check=True)
return True
except CalledProcessError:
return False

def _spkg_installation_hint(self, spkgs, prompt, feature):
r"""
Return a string that explains how to install ``feature``.
EXAMPLES::
sage: from sage.features import SagePackageSystem
sage: print(SagePackageSystem().spkg_installation_hint(['foo', 'bar'], prompt="### ", feature='foobarability')) # indirect doctest
To install foobarability using the Sage package manager, you can try to run:
### sage -i foo bar
"""
lines = []
lines.append(f'To install {feature} using the Sage package manager, you can try to run:')
lines.append(f'{prompt}sage -i {spkgs}')
return '\n'.join(lines)

class PipPackageSystem(PackageSystem):
r"""
The feature describing the Pip package manager.
EXAMPLES::
sage: from sage.features import PipPackageSystem
sage: PipPackageSystem()
Feature('pip')
"""
@staticmethod
def __classcall__(cls):
r"""
Normalize initargs.
TESTS::
sage: from sage.features import PipPackageSystem
sage: PipPackageSystem() is PipPackageSystem() # indirect doctest
True
"""
return PackageSystem.__classcall__(cls, "pip")

def _is_present(self):
r"""
Test whether ``pip`` is available.
EXAMPLES::
sage: from sage.features import PipPackageSystem
sage: bool(PipPackageSystem().is_present()) # indirect doctest
True
"""
from subprocess import run, DEVNULL, CalledProcessError
try:
run('sage -pip --version', shell=True, stdout=DEVNULL, stderr=DEVNULL, check=True)
return True
except CalledProcessError:
return False

class Executable(Feature):
r"""
A feature describing an executable in the PATH.
A feature describing an executable in the ``PATH``.
.. NOTE::
Expand Down
3 changes: 3 additions & 0 deletions src/sage/features/all.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
r"""
Enumeration of all defined features
"""

def all_features():
r"""
Expand Down
10 changes: 5 additions & 5 deletions src/sage/features/bliss.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
r"""
Checks for bliss
Features for testing the presence of ``bliss``
"""
from . import CythonFeature, PythonModule
from .join_feature import JoinFeature
Expand All @@ -24,13 +24,13 @@

class BlissLibrary(CythonFeature):
r"""
A :class:`Feature` which describes whether the Bliss library is
A :class:`~sage.features.Feature` which describes whether the Bliss library is
present and functional.
EXAMPLES::
sage: from sage.features.bliss import BlissLibrary
sage: BlissLibrary().require() # optional: libbliss
sage: BlissLibrary().require() # optional - libbliss
"""

def __init__(self):
Expand All @@ -48,13 +48,13 @@ def __init__(self):

class Bliss(JoinFeature):
r"""
A :class:`Feature` which describes whether the :mod:`sage.graphs.bliss`
A :class:`~sage.features.Feature` which describes whether the :mod:`sage.graphs.bliss`
module is available in this installation of Sage.
EXAMPLES::
sage: from sage.features.bliss import Bliss
sage: Bliss().require() # optional: bliss
sage: Bliss().require() # optional - bliss
"""
def __init__(self):
r"""
Expand Down
Loading

0 comments on commit d1d603d

Please sign in to comment.