Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce negated optional tag #38810

Merged
merged 7 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions src/doc/en/developer/coding_basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
General Conventions
===================


There are many ways to contribute to Sage, including sharing scripts
and Jupyter notebooks that implement new functionality using Sage,
improving to the Sage library, or to working on the many underlying
Expand Down Expand Up @@ -1256,17 +1255,38 @@ framework. Here is a comprehensive list:
Neither of this applies to files or directories which are explicitly given
as command line arguments: those are always tested.

- **optional/needs:** A line tagged with ``optional - FEATURE``
or ``needs FEATURE`` is not tested unless the ``--optional=KEYWORD`` flag
is passed to ``sage -t`` (see
:ref:`section-optional-doctest-flag`). The main applications are:
- **optional** or **needs:** A line tagged with ``optional - FEATURE`` or
``needs FEATURE`` is tested if the feature is available in Sage. If
``FEATURE`` starts with an exclamation point ``!``, then the condition is
negated, that is, the doctest runs only if the feature is not available.

If the feature is included in the ``--optional=KEYWORD`` flag passed to
``sage -t`` (see :ref:`section-optional-doctest-flag`), then the line is
tested regardless of the feature availability.

The main applications are:

- **optional packages:** When a line requires an optional package to be
installed (e.g. the ``sloane_database`` package)::
installed (e.g. the ``rubiks`` package)::

sage: C = RubiksCube("R*L")
sage: C.solve() # optional - rubiks (a hybrid algorithm is used)
'L R'
sage: C.solve() # optional - !rubiks (GAP is used)
'L*R'

- **features:** When a line requires a feature to be present::

sage: SloaneEncyclopedia[60843] # optional - sloane_database
[1, 6, 21, 107, 47176870]

sage: SloaneEncyclopedia[60843] # optional - !sloane_database
Traceback (most recent call last):
...
OSError: The Sloane Encyclopedia database must be installed. Use e.g.
'SloaneEncyclopedia.install()' to download and install it.

- **internet:** For lines that require an internet connection::
For lines that require an internet connection::

sage: oeis(60843) # optional - internet
A060843: Busy Beaver problem: a(n) = maximal number of steps that an
Expand Down
22 changes: 18 additions & 4 deletions src/sage/databases/sloane.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
::

sage: SloaneEncyclopedia[60843] # optional - sloane_database
[1, 6, 21, 107]
[1, 6, 21, 107, 47176870]

To get the name of a sequence, type

Expand Down Expand Up @@ -149,6 +149,17 @@
self.load()
return len(self.__data__)

def is_installed(self):
"""
Check if a local copy of the encyclopedia is installed.

EXAMPLES::

sage: SloaneEncyclopedia.is_installed() # optional - sloane_database
True
"""
return os.path.exists(self.__file__) and os.path.exists(self.__file_names__)

def find(self, seq, maxresults=30):
"""
Return a list of all sequences which have seq as a subsequence, up
Expand Down Expand Up @@ -274,7 +285,7 @@
for L in file_seq:
if len(L) == 0:
continue
m = entry.search(L)
m = entry.search(L.decode('utf-8'))

Check warning on line 288 in src/sage/databases/sloane.py

View check run for this annotation

Codecov / codecov/patch

src/sage/databases/sloane.py#L288

Added line #L288 was not covered by tests
if m:
seqnum = int(m.group('num'))
msg = m.group('body').strip()
Expand All @@ -287,10 +298,13 @@
for L in file_names:
if not L:
continue
m = entry.search(L)
m = entry.search(L.decode('utf-8'))

Check warning on line 301 in src/sage/databases/sloane.py

View check run for this annotation

Codecov / codecov/patch

src/sage/databases/sloane.py#L301

Added line #L301 was not covered by tests
if m:
seqnum = int(m.group('num'))
self.__data__[seqnum][3] = m.group('body').strip()
if seqnum in self.__data__:
self.__data__[seqnum][3] = m.group('body').strip()

Check warning on line 305 in src/sage/databases/sloane.py

View check run for this annotation

Codecov / codecov/patch

src/sage/databases/sloane.py#L304-L305

Added lines #L304 - L305 were not covered by tests
else:
self.__data__[seqnum] = [seqnum, None, 'unknown', m.group('body').strip()]

Check warning on line 307 in src/sage/databases/sloane.py

View check run for this annotation

Codecov / codecov/patch

src/sage/databases/sloane.py#L307

Added line #L307 was not covered by tests
file_names.close()
self.__loaded_names__ = True
except KeyError:
Expand Down
18 changes: 9 additions & 9 deletions src/sage/doctest/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
special_optional_regex = (
"py2|long time|not implemented|not tested|optional|needs|known bug"
)
tag_with_explanation_regex = r"((?:\w|[.])*)\s*(?:\((?P<cmd_explanation>.*?)\))?"
tag_with_explanation_regex = r"((?:!?\w|[.])*)\s*(?:\((?P<cmd_explanation>.*?)\))?"
optional_regex = re.compile(
rf"[^ a-z]\s*(?P<cmd>{special_optional_regex})(?:\s|[:-])*(?P<tags>(?:(?:{tag_with_explanation_regex})\s*)*)",
re.IGNORECASE,
Expand Down Expand Up @@ -1124,14 +1124,14 @@
continue

if self.optional_tags is not True:
extra = {
tag
for tag in optional_tags
if (
tag not in self.optional_tags
and tag not in available_software
)
}
extra = set()
for tag in optional_tags:
if tag not in self.optional_tags:
if tag.startswith('!'):
if tag[1:] in available_software:
extra.add(tag)

Check warning on line 1132 in src/sage/doctest/parsing.py

View check run for this annotation

Codecov / codecov/patch

src/sage/doctest/parsing.py#L1132

Added line #L1132 was not covered by tests
elif tag not in available_software:
extra.add(tag)
if extra and any(tag in ["bug"] for tag in extra):
# Bug only occurs on a specific platform?
bug_platform = optional_tags_with_values.get("bug")
Expand Down
42 changes: 42 additions & 0 deletions src/sage/features/dot2tex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# sage_setup: distribution = sagemath-environment
r"""
Check for ``dot2tex``
"""

# *****************************************************************************
# Copyright (C) 2024 Kwankyu Lee
#
# Distributed under the terms of the GNU General Public License (GPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# https://www.gnu.org/licenses/
# *****************************************************************************

from . import PythonModule


class dot2tex(PythonModule):
r"""
A :class:`sage.features.Feature` describing the presence of :ref:`dot2tex <spkg_dot2tex>`.

dot2tex is provided by an optional package in the Sage distribution.

EXAMPLES::

sage: from sage.features.dot2tex import dot2tex
sage: dot2tex().is_present() # optional - dot2tex
FeatureTestResult('dot2tex', True)
"""
def __init__(self):
r"""
TESTS::

sage: from sage.features.dot2tex import dot2tex
sage: isinstance(dot2tex(), dot2tex)
True
"""
PythonModule.__init__(self, 'dot2tex', spkg='dot2tex')


def all_features():
return [dot2tex()]
59 changes: 59 additions & 0 deletions src/sage/features/sloane_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# sage_setup: distribution = sagemath-environment
r"""
Feature for testing the presence of Sloane Online Encyclopedia of Integer Sequences
"""

# ****************************************************************************
# Copyright (C) 2024 Kwankyu Lee <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************

from . import Feature


class SloaneOEIS(Feature):
r"""
A :class:`~sage.features.Feature` which describes the presence of
the Sloane Online Encyclopedia of Integer Sequences.

EXAMPLES::

sage: from sage.features.sloane_database import SloaneOEIS
sage: bool(SloaneOEIS().is_present()) # optional - sloane_database
True
"""
def __init__(self):
r"""
TESTS::

sage: from sage.features.sloane_database import SloaneOEIS
sage: isinstance(SloaneOEIS(), SloaneOEIS)
True
"""
Feature.__init__(self, name='sloane_database',
description='Sloane Online Encyclopedia of Integer Sequences')

def _is_present(self):
r"""
Return whether the database is available.

EXAMPLES::

sage: from sage.features.sloane_database import SloaneOEIS
sage: bool(SloaneOEIS().is_present()) # optional - !sloane_database
False
"""
try:
from sage.databases.sloane import SloaneEncyclopedia
except ImportError:
return False

Check warning on line 54 in src/sage/features/sloane_database.py

View check run for this annotation

Codecov / codecov/patch

src/sage/features/sloane_database.py#L53-L54

Added lines #L53 - L54 were not covered by tests
return SloaneEncyclopedia.is_installed()


def all_features():
return [SloaneOEIS()]
2 changes: 1 addition & 1 deletion src/sage/graphs/graph_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ def tkz_picture(self):
For a complicated vertex, a TeX box is used. ::

sage: B = crystals.Tableaux(['B', 2], shape=[1])
sage: latex(B)
sage: latex(B) # optional - !dot2tex
\begin{tikzpicture}
...
\newsavebox{\vertex}
Expand Down
30 changes: 15 additions & 15 deletions src/sage/groups/perm_gps/cubegroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1422,25 +1422,22 @@
return NotImplemented
return richcmp(self._state, other._state, op)

def solve(self, algorithm='hybrid', timeout=15):
def solve(self, algorithm='default', timeout=15):
r"""
Solve the Rubik's cube.

INPUT:

- ``algorithm`` -- must be one of the following:

- ``hybrid`` -- try ``kociemba`` for timeout seconds, then ``dietz``
- ``kociemba`` -- use Dik T. Winter's program
(reasonable speed, few moves)
- ``dietz`` -- use Eric Dietz's cubex program
(fast but lots of moves)
- ``optimal`` -- use Michael Reid's optimal program
(may take a long time)
- ``hybrid`` -- (default) try ``kociemba`` for timeout seconds, then ``dietz``
- ``kociemba`` -- use Dik T. Winter's program (reasonable speed, few moves)
- ``dietz`` -- use Eric Dietz's cubex program (fast but lots of moves)
- ``optimal`` -- use Michael Reid's optimal program (may take a long time)
- ``gap`` -- use GAP word solution (can be slow)

Any choice other than ``gap`` requires the optional package
``rubiks``. Otherwise, the ``gap`` algorithm is used.
Any choice other than ``gap`` requires the optional package ``rubiks``.
If the package is not installed, the ``gap`` algorithm is used by default.

EXAMPLES::

Expand All @@ -1452,19 +1449,22 @@
solutions::

sage: s = C.solve('dietz'); s # optional - rubiks
"U' L' L' U L U' L U D L L D' L' D L' D' L D L' U' L D' L' U L' B' U' L' U B L D L D' U' L' U L B L B' L' U L U' L' F' L' F L' F L F' L' D' L' D D L D' B L B' L B' L B F' L F F B' L F' B D' D' L D B' B' L' D' B U' U' L' B' D' F' F' L D F'"
"U' L' L' U L U' L U D L L D' L' D L' D' L D L' U' L D' L' U L' B'
U' L' U B L D L D' U' L' U L B L B' L' U L U' L' F' L' F L' F L F'
L' D' L' D D L D' B L B' L B' L B F' L F F B' L F' B D' D' L D B'
B' L' D' B U' U' L' B' D' F' F' L D F'"
sage: C2 = RubiksCube(s) # optional - rubiks
sage: C == C2 # optional - rubiks
True
"""
from sage.features.rubiks import Rubiks
if Rubiks().is_present():
import sage.interfaces.rubik # here to avoid circular referencing
if algorithm == 'default':
algorithm = "hybrid"

Check warning on line 1464 in src/sage/groups/perm_gps/cubegroup.py

View check run for this annotation

Codecov / codecov/patch

src/sage/groups/perm_gps/cubegroup.py#L1463-L1464

Added lines #L1463 - L1464 were not covered by tests
else:
algorithm = 'gap'

if algorithm == "default":
algorithm = "hybrid"
if algorithm == 'default':
algorithm = 'gap'

if algorithm == "hybrid":
try:
Expand Down
Loading