passagemath-repl 10.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_repl-10.5.1.data/scripts/sage-cachegrind +25 -0
- passagemath_repl-10.5.1.data/scripts/sage-callgrind +16 -0
- passagemath_repl-10.5.1.data/scripts/sage-cleaner +230 -0
- passagemath_repl-10.5.1.data/scripts/sage-coverage +327 -0
- passagemath_repl-10.5.1.data/scripts/sage-eval +14 -0
- passagemath_repl-10.5.1.data/scripts/sage-fixdoctests +710 -0
- passagemath_repl-10.5.1.data/scripts/sage-inline-fortran +12 -0
- passagemath_repl-10.5.1.data/scripts/sage-ipynb2rst +50 -0
- passagemath_repl-10.5.1.data/scripts/sage-ipython +16 -0
- passagemath_repl-10.5.1.data/scripts/sage-massif +25 -0
- passagemath_repl-10.5.1.data/scripts/sage-notebook +267 -0
- passagemath_repl-10.5.1.data/scripts/sage-omega +25 -0
- passagemath_repl-10.5.1.data/scripts/sage-preparse +302 -0
- passagemath_repl-10.5.1.data/scripts/sage-run +27 -0
- passagemath_repl-10.5.1.data/scripts/sage-run-cython +10 -0
- passagemath_repl-10.5.1.data/scripts/sage-runtests +9 -0
- passagemath_repl-10.5.1.data/scripts/sage-startuptime.py +163 -0
- passagemath_repl-10.5.1.data/scripts/sage-valgrind +34 -0
- passagemath_repl-10.5.1.dist-info/METADATA +77 -0
- passagemath_repl-10.5.1.dist-info/RECORD +162 -0
- passagemath_repl-10.5.1.dist-info/WHEEL +5 -0
- passagemath_repl-10.5.1.dist-info/top_level.txt +1 -0
- sage/all__sagemath_repl.py +119 -0
- sage/doctest/__init__.py +4 -0
- sage/doctest/__main__.py +236 -0
- sage/doctest/all.py +4 -0
- sage/doctest/check_tolerance.py +261 -0
- sage/doctest/control.py +1727 -0
- sage/doctest/external.py +534 -0
- sage/doctest/fixtures.py +383 -0
- sage/doctest/forker.py +2665 -0
- sage/doctest/marked_output.py +102 -0
- sage/doctest/parsing.py +1708 -0
- sage/doctest/parsing_test.py +79 -0
- sage/doctest/reporting.py +733 -0
- sage/doctest/rif_tol.py +124 -0
- sage/doctest/sources.py +1657 -0
- sage/doctest/test.py +584 -0
- sage/doctest/tests/1second.rst +4 -0
- sage/doctest/tests/99seconds.rst +4 -0
- sage/doctest/tests/abort.rst +5 -0
- sage/doctest/tests/atexit.rst +7 -0
- sage/doctest/tests/fail_and_die.rst +6 -0
- sage/doctest/tests/initial.rst +15 -0
- sage/doctest/tests/interrupt.rst +7 -0
- sage/doctest/tests/interrupt_diehard.rst +14 -0
- sage/doctest/tests/keyboardinterrupt.rst +11 -0
- sage/doctest/tests/longtime.rst +5 -0
- sage/doctest/tests/nodoctest +5 -0
- sage/doctest/tests/random_seed.rst +4 -0
- sage/doctest/tests/show_skipped.rst +18 -0
- sage/doctest/tests/sig_on.rst +9 -0
- sage/doctest/tests/simple_failure.rst +8 -0
- sage/doctest/tests/sleep_and_raise.rst +106 -0
- sage/doctest/tests/tolerance.rst +31 -0
- sage/doctest/util.py +750 -0
- sage/interfaces/cleaner.py +48 -0
- sage/interfaces/quit.py +163 -0
- sage/misc/all__sagemath_repl.py +51 -0
- sage/misc/banner.py +235 -0
- sage/misc/benchmark.py +221 -0
- sage/misc/classgraph.py +134 -0
- sage/misc/copying.py +22 -0
- sage/misc/cython.py +694 -0
- sage/misc/dev_tools.py +745 -0
- sage/misc/edit_module.py +304 -0
- sage/misc/explain_pickle.py +3079 -0
- sage/misc/gperftools.py +361 -0
- sage/misc/inline_fortran.py +212 -0
- sage/misc/messaging.py +86 -0
- sage/misc/pager.py +21 -0
- sage/misc/profiler.py +179 -0
- sage/misc/python.py +70 -0
- sage/misc/remote_file.py +53 -0
- sage/misc/sage_eval.py +249 -0
- sage/misc/sage_input.py +3621 -0
- sage/misc/sagedoc.py +1742 -0
- sage/misc/sh.py +38 -0
- sage/misc/trace.py +90 -0
- sage/repl/__init__.py +16 -0
- sage/repl/all.py +15 -0
- sage/repl/attach.py +625 -0
- sage/repl/configuration.py +186 -0
- sage/repl/display/__init__.py +1 -0
- sage/repl/display/fancy_repr.py +354 -0
- sage/repl/display/formatter.py +318 -0
- sage/repl/display/jsmol_iframe.py +290 -0
- sage/repl/display/pretty_print.py +153 -0
- sage/repl/display/util.py +163 -0
- sage/repl/image.py +302 -0
- sage/repl/inputhook.py +91 -0
- sage/repl/interface_magic.py +298 -0
- sage/repl/interpreter.py +854 -0
- sage/repl/ipython_extension.py +593 -0
- sage/repl/ipython_kernel/__init__.py +1 -0
- sage/repl/ipython_kernel/__main__.py +4 -0
- sage/repl/ipython_kernel/all_jupyter.py +10 -0
- sage/repl/ipython_kernel/install.py +301 -0
- sage/repl/ipython_kernel/interact.py +278 -0
- sage/repl/ipython_kernel/kernel.py +217 -0
- sage/repl/ipython_kernel/widgets.py +466 -0
- sage/repl/ipython_kernel/widgets_sagenb.py +587 -0
- sage/repl/ipython_tests.py +163 -0
- sage/repl/load.py +326 -0
- sage/repl/preparse.py +2218 -0
- sage/repl/prompts.py +90 -0
- sage/repl/rich_output/__init__.py +4 -0
- sage/repl/rich_output/backend_base.py +648 -0
- sage/repl/rich_output/backend_doctest.py +316 -0
- sage/repl/rich_output/backend_emacs.py +151 -0
- sage/repl/rich_output/backend_ipython.py +596 -0
- sage/repl/rich_output/buffer.py +311 -0
- sage/repl/rich_output/display_manager.py +829 -0
- sage/repl/rich_output/example.avi +0 -0
- sage/repl/rich_output/example.canvas3d +1 -0
- sage/repl/rich_output/example.dvi +0 -0
- sage/repl/rich_output/example.flv +0 -0
- sage/repl/rich_output/example.gif +0 -0
- sage/repl/rich_output/example.jpg +0 -0
- sage/repl/rich_output/example.mkv +0 -0
- sage/repl/rich_output/example.mov +0 -0
- sage/repl/rich_output/example.mp4 +0 -0
- sage/repl/rich_output/example.ogv +0 -0
- sage/repl/rich_output/example.pdf +0 -0
- sage/repl/rich_output/example.png +0 -0
- sage/repl/rich_output/example.svg +54 -0
- sage/repl/rich_output/example.webm +0 -0
- sage/repl/rich_output/example.wmv +0 -0
- sage/repl/rich_output/example_jmol.spt.zip +0 -0
- sage/repl/rich_output/example_wavefront_scene.mtl +7 -0
- sage/repl/rich_output/example_wavefront_scene.obj +17 -0
- sage/repl/rich_output/output_basic.py +391 -0
- sage/repl/rich_output/output_browser.py +103 -0
- sage/repl/rich_output/output_catalog.py +54 -0
- sage/repl/rich_output/output_graphics.py +320 -0
- sage/repl/rich_output/output_graphics3d.py +345 -0
- sage/repl/rich_output/output_video.py +231 -0
- sage/repl/rich_output/preferences.py +432 -0
- sage/repl/rich_output/pretty_print.py +339 -0
- sage/repl/rich_output/test_backend.py +201 -0
- sage/repl/user_globals.py +214 -0
- sage/tests/all.py +0 -0
- sage/tests/all__sagemath_repl.py +3 -0
- sage/tests/article_heuberger_krenn_kropf_fsm-in-sage.py +630 -0
- sage/tests/arxiv_0812_2725.py +351 -0
- sage/tests/benchmark.py +1925 -0
- sage/tests/book_schilling_zabrocki_kschur_primer.py +795 -0
- sage/tests/book_stein_ent.py +651 -0
- sage/tests/book_stein_modform.py +558 -0
- sage/tests/cmdline.py +796 -0
- sage/tests/combinatorial_hopf_algebras.py +52 -0
- sage/tests/finite_poset.py +623 -0
- sage/tests/functools_partial_src.py +27 -0
- sage/tests/gosper-sum.py +218 -0
- sage/tests/lazy_imports.py +28 -0
- sage/tests/modular_group_cohomology.py +80 -0
- sage/tests/numpy.py +21 -0
- sage/tests/parigp.py +76 -0
- sage/tests/startup.py +27 -0
- sage/tests/symbolic-series.py +76 -0
- sage/tests/sympy.py +16 -0
- sage/tests/test_deprecation.py +31 -0
sage/doctest/__main__.py
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-repl
|
2
|
+
import argparse
|
3
|
+
import os
|
4
|
+
import sys
|
5
|
+
|
6
|
+
# Note: the DOT_SAGE and SAGE_STARTUP_FILE environment variables have already been set by sage-env
|
7
|
+
DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'),
|
8
|
+
'.sage'))
|
9
|
+
|
10
|
+
# Override to not pick up user configuration, see Issue #20270
|
11
|
+
os.environ['SAGE_STARTUP_FILE'] = os.path.join(DOT_SAGE, 'init-doctests.sage')
|
12
|
+
|
13
|
+
|
14
|
+
def _get_optional_defaults():
|
15
|
+
"""Return the default value for the --optional flag."""
|
16
|
+
optional = ['sage', 'optional']
|
17
|
+
|
18
|
+
return ','.join(optional)
|
19
|
+
|
20
|
+
|
21
|
+
def _make_parser():
|
22
|
+
r"""
|
23
|
+
Return the :class:`argparse.ArgumentParser`.
|
24
|
+
|
25
|
+
TESTS:
|
26
|
+
|
27
|
+
Test that the defaults are the consistent::
|
28
|
+
|
29
|
+
sage: from sage.doctest.control import DocTestDefaults
|
30
|
+
sage: from sage.doctest.__main__ import _make_parser
|
31
|
+
sage: os.environ.pop('SAGE_DOCTEST_RANDOM_SEED', None)
|
32
|
+
...
|
33
|
+
sage: parser = _make_parser()
|
34
|
+
sage: args = parser.parse_args([])
|
35
|
+
sage: DD = DocTestDefaults(runtest_default=True); DD
|
36
|
+
DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0,
|
37
|
+
optional='sage,optional', random_seed=None,
|
38
|
+
stats_path='.../timings2.json')
|
39
|
+
sage: D = copy(args.__dict__)
|
40
|
+
sage: del D['filenames']
|
41
|
+
sage: DA = DocTestDefaults(runtest_default=True, **D); DA
|
42
|
+
DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0,
|
43
|
+
optional='sage,optional', random_seed=None,
|
44
|
+
stats_path='.../timings2.json')
|
45
|
+
"""
|
46
|
+
parser = argparse.ArgumentParser(usage="sage -t [options] filenames",
|
47
|
+
description="Run all tests in a file or a list of files whose extensions "
|
48
|
+
"are one of the following: "
|
49
|
+
".py, .pyx, .pxd, .pxi, .sage, .spyx, .tex, .rst.")
|
50
|
+
parser.add_argument("-p", "--nthreads", dest="nthreads",
|
51
|
+
type=int, nargs='?', const=0, default=1, metavar="N",
|
52
|
+
help="test in parallel using N threads, with 0 interpreted as max(2, min(8, cpu_count())); "
|
53
|
+
"when run under the control of the GNU make jobserver (make -j), request as most N job slots")
|
54
|
+
parser.add_argument("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file, 0 for no timeout")
|
55
|
+
what = parser.add_mutually_exclusive_group()
|
56
|
+
what.add_argument("-a", "--all", action="store_true", default=False, help="test all files in the Sage library")
|
57
|
+
what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library")
|
58
|
+
parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE")
|
59
|
+
|
60
|
+
parser.add_argument("--format", choices=["sage", "github"], default="sage",
|
61
|
+
help="set format of error messages and warnings")
|
62
|
+
parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'")
|
63
|
+
parser.add_argument("-s", "--short", dest="target_walltime", nargs='?',
|
64
|
+
type=int, default=-1, const=300, metavar="SECONDS",
|
65
|
+
help="run as many doctests as possible in about 300 seconds (or the number of seconds given as an optional argument)")
|
66
|
+
parser.add_argument("--warn-long", dest="warn_long", nargs='?',
|
67
|
+
type=float, default=-1.0, const=1.0, metavar="SECONDS",
|
68
|
+
help="warn if tests take more CPU time than SECONDS")
|
69
|
+
# By default, include all tests marked 'sagemath_doc_html' -- see
|
70
|
+
# https://github.com/sagemath/sage/issues/25345 and
|
71
|
+
# https://github.com/sagemath/sage/issues/26110:
|
72
|
+
parser.add_argument("--optional", metavar="FEATURES", default=_get_optional_defaults(),
|
73
|
+
help='only run tests including one of the "# optional" tags listed in FEATURES (separated by commas); '
|
74
|
+
'if "sage" is listed, will also run the standard doctests; '
|
75
|
+
'if "sagemath_doc_html" is listed, will also run the tests relying on the HTML documentation; '
|
76
|
+
'if "optional" is listed, will also run tests for installed optional packages or detected features; '
|
77
|
+
'if "external" is listed, will also run tests for available external software; '
|
78
|
+
'if set to "all", then all tests will be run; '
|
79
|
+
'use "!FEATURE" to disable tests marked "# optional - FEATURE". '
|
80
|
+
'Note that "!" needs to be quoted or escaped in the shell.')
|
81
|
+
parser.add_argument("--hide", metavar="FEATURES", default="",
|
82
|
+
help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; '
|
83
|
+
'if "all" is listed, will also hide features corresponding to all optional or experimental packages; '
|
84
|
+
'if "optional" is listed, will also hide features corresponding to optional packages.')
|
85
|
+
parser.add_argument("--probe", metavar="FEATURES", default="",
|
86
|
+
help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; '
|
87
|
+
'report the tests that pass nevertheless')
|
88
|
+
parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests")
|
89
|
+
parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests",
|
90
|
+
default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED"))
|
91
|
+
parser.add_argument("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times")
|
92
|
+
parser.add_argument("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure")
|
93
|
+
parser.add_argument("--environment", type=str, default="sage.repl.ipython_kernel.all_jupyter", help="name of a module that provides the global environment for tests")
|
94
|
+
|
95
|
+
parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file")
|
96
|
+
parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception")
|
97
|
+
parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)")
|
98
|
+
parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules")
|
99
|
+
parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths")
|
100
|
+
parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test")
|
101
|
+
parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised")
|
102
|
+
parser.add_argument("--only-errors", action="store_true", default=False, help="only output failures, not test successes")
|
103
|
+
|
104
|
+
parser.add_argument("--gdb", action="store_true", default=False, help="run doctests under the control of gdb")
|
105
|
+
parser.add_argument("--lldb", action="store_true", default=False, help="run doctests under the control of lldb")
|
106
|
+
parser.add_argument("--valgrind", "--memcheck", action="store_true", default=False,
|
107
|
+
help="run doctests using Valgrind's memcheck tool. The log "
|
108
|
+
"files are named sage-memcheck.PID and can be found in " +
|
109
|
+
os.path.join(DOT_SAGE, "valgrind"))
|
110
|
+
parser.add_argument("--massif", action="store_true", default=False,
|
111
|
+
help="run doctests using Valgrind's massif tool. The log "
|
112
|
+
"files are named sage-massif.PID and can be found in " +
|
113
|
+
os.path.join(DOT_SAGE, "valgrind"))
|
114
|
+
parser.add_argument("--cachegrind", action="store_true", default=False,
|
115
|
+
help="run doctests using Valgrind's cachegrind tool. The log "
|
116
|
+
"files are named sage-cachegrind.PID and can be found in " +
|
117
|
+
os.path.join(DOT_SAGE, "valgrind"))
|
118
|
+
parser.add_argument("--omega", action="store_true", default=False,
|
119
|
+
help="run doctests using Valgrind's omega tool. The log "
|
120
|
+
"files are named sage-omega.PID and can be found in " +
|
121
|
+
os.path.join(DOT_SAGE, "valgrind"))
|
122
|
+
|
123
|
+
parser.add_argument("-f", "--failed", action="store_true", default=False,
|
124
|
+
help="doctest only those files that failed in the previous run")
|
125
|
+
what.add_argument("-n", "--new", action="store_true", default=False,
|
126
|
+
help="doctest only those files that have been changed in the repository and not yet been committed")
|
127
|
+
parser.add_argument("--show-skipped", "--show_skipped", action="store_true", default=False,
|
128
|
+
help="print a summary at the end of each file of optional tests that were skipped")
|
129
|
+
|
130
|
+
parser.add_argument("--stats_path", "--stats-path", default=os.path.join(DOT_SAGE, "timings2.json"),
|
131
|
+
help="path to a json dictionary for timings and failure status for each file from previous runs; it will be updated in this run")
|
132
|
+
parser.add_argument("--baseline_stats_path", "--baseline-stats-path", default=None,
|
133
|
+
help="path to a json dictionary for timings and failure status for each file, to be used as a baseline; it will not be updated")
|
134
|
+
|
135
|
+
class GCAction(argparse.Action):
|
136
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
137
|
+
gcopts = dict(DEFAULT=0, ALWAYS=1, NEVER=-1)
|
138
|
+
new_value = gcopts[values]
|
139
|
+
setattr(namespace, self.dest, new_value)
|
140
|
+
|
141
|
+
parser.add_argument("--gc",
|
142
|
+
choices=["DEFAULT", "ALWAYS", "NEVER"],
|
143
|
+
default=0,
|
144
|
+
action=GCAction,
|
145
|
+
help="control garbarge collection "
|
146
|
+
"(ALWAYS: collect garbage before every test; NEVER: disable gc; DEFAULT: Python default)")
|
147
|
+
|
148
|
+
# The --serial option is only really for internal use, better not
|
149
|
+
# document it.
|
150
|
+
parser.add_argument("--serial", action="store_true", default=False, help=argparse.SUPPRESS)
|
151
|
+
# Same for --die_timeout
|
152
|
+
parser.add_argument("--die_timeout", type=int, default=-1, help=argparse.SUPPRESS)
|
153
|
+
|
154
|
+
parser.add_argument("filenames", help="file names", nargs='*')
|
155
|
+
return parser
|
156
|
+
|
157
|
+
|
158
|
+
def main():
|
159
|
+
parser = _make_parser()
|
160
|
+
# custom treatment to separate properly
|
161
|
+
# one or several file names at the end
|
162
|
+
new_arguments = []
|
163
|
+
need_filenames = True
|
164
|
+
in_filenames = False
|
165
|
+
afterlog = False
|
166
|
+
for arg in sys.argv[1:]:
|
167
|
+
if arg in ('-n', '--new', '-a', '--all', '--installed'):
|
168
|
+
need_filenames = False
|
169
|
+
elif need_filenames and not (afterlog or in_filenames) and os.path.exists(arg):
|
170
|
+
in_filenames = True
|
171
|
+
new_arguments.append('--')
|
172
|
+
new_arguments.append(arg)
|
173
|
+
afterlog = arg in ['--logfile', '--stats_path', '--stats-path',
|
174
|
+
'--baseline_stats_path', '--baseline-stats-path']
|
175
|
+
|
176
|
+
args = parser.parse_args(new_arguments)
|
177
|
+
|
178
|
+
if not args.filenames and not (args.all or args.new or args.installed):
|
179
|
+
print('either use --new, --all, --installed, or some filenames')
|
180
|
+
return 2
|
181
|
+
|
182
|
+
# Limit the number of threads to 2 to save system resources.
|
183
|
+
# See Issue #23713, #23892, #30351
|
184
|
+
if sys.platform == 'darwin':
|
185
|
+
os.environ["OMP_NUM_THREADS"] = "1"
|
186
|
+
else:
|
187
|
+
os.environ["OMP_NUM_THREADS"] = "2"
|
188
|
+
|
189
|
+
os.environ["SAGE_NUM_THREADS"] = "2"
|
190
|
+
|
191
|
+
from sage.doctest.control import DocTestController
|
192
|
+
DC = DocTestController(args, args.filenames)
|
193
|
+
err = DC.run()
|
194
|
+
|
195
|
+
# Issue #33521: Do not run pytest if the pytest configuration is not available.
|
196
|
+
# This happens when the source tree is not available and SAGE_SRC falls back
|
197
|
+
# to SAGE_LIB.
|
198
|
+
from sage.env import SAGE_SRC
|
199
|
+
if not all(os.path.isfile(os.path.join(SAGE_SRC, f))
|
200
|
+
for f in ["conftest.py", "tox.ini"]):
|
201
|
+
return err
|
202
|
+
|
203
|
+
try:
|
204
|
+
exit_code_pytest = 0
|
205
|
+
import pytest
|
206
|
+
pytest_options = []
|
207
|
+
if args.verbose:
|
208
|
+
pytest_options.append("-v")
|
209
|
+
|
210
|
+
# #35999: no filename in arguments defaults to "src"
|
211
|
+
if not args.filenames:
|
212
|
+
filenames = [SAGE_SRC]
|
213
|
+
else:
|
214
|
+
# #31924: Do not run pytest on individual Python files unless
|
215
|
+
# they match the pytest file pattern. However, pass names
|
216
|
+
# of directories. We use 'not os.path.isfile(f)' for this so that
|
217
|
+
# we do not silently hide typos.
|
218
|
+
filenames = [f for f in args.filenames
|
219
|
+
if f.endswith("_test.py") or not os.path.isfile(f)]
|
220
|
+
if filenames:
|
221
|
+
print(f"Running pytest on {filenames} with options {pytest_options}")
|
222
|
+
exit_code_pytest = pytest.main(filenames + pytest_options)
|
223
|
+
if exit_code_pytest == 5:
|
224
|
+
# Exit code 5 means there were no test files, pass in this case
|
225
|
+
exit_code_pytest = 0
|
226
|
+
|
227
|
+
except ModuleNotFoundError:
|
228
|
+
print("pytest is not installed in the venv, skip checking tests that rely on it")
|
229
|
+
|
230
|
+
if err == 0:
|
231
|
+
return exit_code_pytest
|
232
|
+
return err
|
233
|
+
|
234
|
+
|
235
|
+
if __name__ == "__main__":
|
236
|
+
sys.exit(main())
|
sage/doctest/all.py
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-repl
|
2
|
+
"""
|
3
|
+
Check tolerance when parsing docstrings
|
4
|
+
"""
|
5
|
+
|
6
|
+
# ****************************************************************************
|
7
|
+
# Copyright (C) 2012-2018 David Roe <roed.math@gmail.com>
|
8
|
+
# 2012 Robert Bradshaw <robertwb@gmail.com>
|
9
|
+
# 2012 William Stein <wstein@gmail.com>
|
10
|
+
# 2013 R. Andrew Ohana
|
11
|
+
# 2013 Volker Braun
|
12
|
+
# 2013-2018 Jeroen Demeyer <jdemeyer@cage.ugent.be>
|
13
|
+
# 2016-2021 Frédéric Chapoton
|
14
|
+
# 2017-2018 Erik M. Bray
|
15
|
+
# 2020 Marc Mezzarobba
|
16
|
+
# 2020-2023 Matthias Koeppe
|
17
|
+
# 2022 John H. Palmieri
|
18
|
+
# 2022 Sébastien Labbé
|
19
|
+
# 2023 Kwankyu Lee
|
20
|
+
#
|
21
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
22
|
+
# as published by the Free Software Foundation; either version 2 of
|
23
|
+
# the License, or (at your option) any later version.
|
24
|
+
# https://www.gnu.org/licenses/
|
25
|
+
# ****************************************************************************
|
26
|
+
|
27
|
+
import re
|
28
|
+
from sage.doctest.rif_tol import RIFtol, add_tolerance
|
29
|
+
from sage.doctest.marked_output import MarkedOutput
|
30
|
+
|
31
|
+
|
32
|
+
# Regex pattern for float without the (optional) leading sign
|
33
|
+
float_without_sign = r'((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?'
|
34
|
+
|
35
|
+
|
36
|
+
# Regular expression for floats
|
37
|
+
float_regex = re.compile(r'\s*([+-]?\s*' + float_without_sign + r')')
|
38
|
+
|
39
|
+
|
40
|
+
class ToleranceExceededError(BaseException):
|
41
|
+
pass
|
42
|
+
|
43
|
+
|
44
|
+
def check_tolerance_real_domain(want: MarkedOutput, got: str) -> tuple[str, str]:
|
45
|
+
"""
|
46
|
+
Compare want and got over real domain with tolerance
|
47
|
+
|
48
|
+
INPUT:
|
49
|
+
|
50
|
+
- ``want`` -- a string, what you want
|
51
|
+
- ``got`` -- a string, what you got
|
52
|
+
|
53
|
+
OUTPUT:
|
54
|
+
|
55
|
+
The strings to compare, but with matching float numbers replaced by asterisk.
|
56
|
+
|
57
|
+
EXAMPLES::
|
58
|
+
|
59
|
+
sage: # needs sage.rings.real_interval_field
|
60
|
+
sage: from sage.doctest.check_tolerance import check_tolerance_real_domain
|
61
|
+
sage: from sage.doctest.marked_output import MarkedOutput
|
62
|
+
sage: check_tolerance_real_domain(
|
63
|
+
....: MarkedOutput('foo:0.2').update(abs_tol=0.3),
|
64
|
+
....: 'bar:0.4')
|
65
|
+
['foo:*', 'bar:*']
|
66
|
+
sage: check_tolerance_real_domain(
|
67
|
+
....: MarkedOutput('foo:0.2').update(abs_tol=0.3),
|
68
|
+
....: 'bar:0.6')
|
69
|
+
Traceback (most recent call last):
|
70
|
+
...
|
71
|
+
sage.doctest.check_tolerance.ToleranceExceededError
|
72
|
+
"""
|
73
|
+
# First check that the number of occurrences of floats appearing match
|
74
|
+
want_str = [g[0] for g in float_regex.findall(want)]
|
75
|
+
got_str = [g[0] for g in float_regex.findall(got)]
|
76
|
+
if len(want_str) != len(got_str):
|
77
|
+
raise ToleranceExceededError()
|
78
|
+
|
79
|
+
# Then check the numbers
|
80
|
+
want_values = [RIFtol(g) for g in want_str]
|
81
|
+
want_intervals = [add_tolerance(v, want) for v in want_values]
|
82
|
+
got_values = [RIFtol(g) for g in got_str]
|
83
|
+
# The doctest is not successful if one of the "want" and "got"
|
84
|
+
# intervals have an empty intersection
|
85
|
+
if not all(a.overlaps(b) for a, b in zip(want_intervals, got_values)):
|
86
|
+
raise ToleranceExceededError()
|
87
|
+
|
88
|
+
# Then check the part of the doctests without the numbers
|
89
|
+
# Continue the check process with floats replaced by stars
|
90
|
+
want = float_regex.sub('*', want)
|
91
|
+
got = float_regex.sub('*', got)
|
92
|
+
return [want, got]
|
93
|
+
|
94
|
+
|
95
|
+
# match 1.0 or 1.0 + I or 1.0 + 2.0*I
|
96
|
+
real_plus_optional_imag = ''.join([
|
97
|
+
r'\s*(?P<real>[+-]?\s*',
|
98
|
+
float_without_sign,
|
99
|
+
r')(\s*(?P<real_imag_coeff>[+-]\s*',
|
100
|
+
float_without_sign,
|
101
|
+
r')\*I|\s*(?P<real_imag_unit>[+-])\s*I)?',
|
102
|
+
])
|
103
|
+
|
104
|
+
|
105
|
+
# match - 2.0*I
|
106
|
+
only_imag = ''.join([
|
107
|
+
r'\s*(?P<only_imag>[+-]?\s*',
|
108
|
+
float_without_sign,
|
109
|
+
r')\*I',
|
110
|
+
])
|
111
|
+
|
112
|
+
|
113
|
+
# match I or -I (no digits), require a non-word part before and after for specificity
|
114
|
+
imaginary_unit = r'(?P<unit_imag_pre>^|\W)(?P<unit_imag>[+-]?)I(?P<unit_imag_post>$|\W)'
|
115
|
+
|
116
|
+
|
117
|
+
complex_regex = re.compile(''.join([
|
118
|
+
'(',
|
119
|
+
only_imag,
|
120
|
+
'|',
|
121
|
+
imaginary_unit,
|
122
|
+
'|',
|
123
|
+
real_plus_optional_imag,
|
124
|
+
')',
|
125
|
+
]))
|
126
|
+
|
127
|
+
|
128
|
+
def complex_match_to_real_and_imag(m: re.Match) -> tuple[str, str]:
|
129
|
+
"""
|
130
|
+
Extract real and imaginary part from match
|
131
|
+
|
132
|
+
INPUT:
|
133
|
+
|
134
|
+
- ``m`` -- match from ``complex_regex``
|
135
|
+
|
136
|
+
OUTPUT:
|
137
|
+
|
138
|
+
Pair of real and complex parts (as string)
|
139
|
+
|
140
|
+
EXAMPLES::
|
141
|
+
|
142
|
+
sage: from sage.doctest.check_tolerance import complex_match_to_real_and_imag, complex_regex
|
143
|
+
sage: complex_match_to_real_and_imag(complex_regex.match('1.0'))
|
144
|
+
('1.0', '0')
|
145
|
+
sage: complex_match_to_real_and_imag(complex_regex.match('-1.0 - I'))
|
146
|
+
('-1.0', '-1')
|
147
|
+
sage: complex_match_to_real_and_imag(complex_regex.match('1.0 - 3.0*I'))
|
148
|
+
('1.0', '- 3.0')
|
149
|
+
sage: complex_match_to_real_and_imag(complex_regex.match('1.0*I'))
|
150
|
+
('0', '1.0')
|
151
|
+
sage: complex_match_to_real_and_imag(complex_regex.match('- 2.0*I'))
|
152
|
+
('0', '- 2.0')
|
153
|
+
sage: complex_match_to_real_and_imag(complex_regex.match('-I'))
|
154
|
+
('0', '-1')
|
155
|
+
sage: for match in complex_regex.finditer('[1, -1, I, -1, -I]'):
|
156
|
+
....: print(complex_match_to_real_and_imag(match))
|
157
|
+
('1', '0')
|
158
|
+
('-1', '0')
|
159
|
+
('0', '1')
|
160
|
+
('-1', '0')
|
161
|
+
('0', '-1')
|
162
|
+
sage: for match in complex_regex.finditer('[1, -1.3, -1.5 + 0.1*I, 0.5 - 0.1*I, -1.5*I]'):
|
163
|
+
....: print(complex_match_to_real_and_imag(match))
|
164
|
+
('1', '0')
|
165
|
+
('-1.3', '0')
|
166
|
+
('-1.5', '+ 0.1')
|
167
|
+
('0.5', '- 0.1')
|
168
|
+
('0', '-1.5')
|
169
|
+
"""
|
170
|
+
real = m.group('real')
|
171
|
+
if real is not None:
|
172
|
+
real_imag_coeff = m.group('real_imag_coeff')
|
173
|
+
real_imag_unit = m.group('real_imag_unit')
|
174
|
+
if real_imag_coeff is not None:
|
175
|
+
return (real, real_imag_coeff)
|
176
|
+
elif real_imag_unit is not None:
|
177
|
+
return (real, real_imag_unit + '1')
|
178
|
+
else:
|
179
|
+
return (real, '0')
|
180
|
+
only_imag = m.group('only_imag')
|
181
|
+
if only_imag is not None:
|
182
|
+
return ('0', only_imag)
|
183
|
+
unit_imag = m.group('unit_imag')
|
184
|
+
if unit_imag is not None:
|
185
|
+
return ('0', unit_imag + '1')
|
186
|
+
assert False, 'unreachable'
|
187
|
+
|
188
|
+
|
189
|
+
def complex_star_repl(m: re.Match):
|
190
|
+
"""
|
191
|
+
Replace the complex number in the match with '*'
|
192
|
+
"""
|
193
|
+
if m.group('unit_imag') is not None:
|
194
|
+
# preserve the matched non-word part
|
195
|
+
return ''.join([
|
196
|
+
(m.group('unit_imag_pre') or '').strip(),
|
197
|
+
'*',
|
198
|
+
(m.group('unit_imag_post') or '').strip(),
|
199
|
+
])
|
200
|
+
else:
|
201
|
+
return '*'
|
202
|
+
|
203
|
+
|
204
|
+
def check_tolerance_complex_domain(want: MarkedOutput, got: str) -> tuple[str, str]:
|
205
|
+
"""
|
206
|
+
Compare want and got over complex domain with tolerance
|
207
|
+
|
208
|
+
INPUT:
|
209
|
+
|
210
|
+
- ``want`` -- a string, what you want
|
211
|
+
- ``got`` -- a string, what you got
|
212
|
+
|
213
|
+
OUTPUT:
|
214
|
+
|
215
|
+
The strings to compare, but with matching complex numbers replaced by asterisk.
|
216
|
+
|
217
|
+
EXAMPLES::
|
218
|
+
|
219
|
+
sage: # needs sage.rings.real_interval_field
|
220
|
+
sage: from sage.doctest.check_tolerance import check_tolerance_complex_domain
|
221
|
+
sage: from sage.doctest.marked_output import MarkedOutput
|
222
|
+
sage: check_tolerance_complex_domain(
|
223
|
+
....: MarkedOutput('foo:[0.2 + 0.1*I]').update(abs_tol=0.3),
|
224
|
+
....: 'bar:[0.4]')
|
225
|
+
['foo:[*]', 'bar:[*]']
|
226
|
+
sage: check_tolerance_complex_domain(
|
227
|
+
....: MarkedOutput('foo:-0.5 - 0.1*I').update(abs_tol=2),
|
228
|
+
....: 'bar:1')
|
229
|
+
['foo:*', 'bar:*']
|
230
|
+
sage: check_tolerance_complex_domain(
|
231
|
+
....: MarkedOutput('foo:[1.0*I]').update(abs_tol=0.3),
|
232
|
+
....: 'bar:[I]')
|
233
|
+
['foo:[*]', 'bar:[*]']
|
234
|
+
sage: check_tolerance_complex_domain(MarkedOutput('foo:0.2 + 0.1*I').update(abs_tol=0.3), 'bar:0.6')
|
235
|
+
Traceback (most recent call last):
|
236
|
+
...
|
237
|
+
sage.doctest.check_tolerance.ToleranceExceededError
|
238
|
+
"""
|
239
|
+
want_str = []
|
240
|
+
for match in complex_regex.finditer(want):
|
241
|
+
want_str.extend(complex_match_to_real_and_imag(match))
|
242
|
+
got_str = []
|
243
|
+
for match in complex_regex.finditer(got):
|
244
|
+
got_str.extend(complex_match_to_real_and_imag(match))
|
245
|
+
if len(want_str) != len(got_str):
|
246
|
+
raise ToleranceExceededError()
|
247
|
+
|
248
|
+
# Then check the numbers
|
249
|
+
want_values = [RIFtol(g) for g in want_str]
|
250
|
+
want_intervals = [add_tolerance(v, want) for v in want_values]
|
251
|
+
got_values = [RIFtol(g) for g in got_str]
|
252
|
+
# The doctest is not successful if one of the "want" and "got"
|
253
|
+
# intervals have an empty intersection
|
254
|
+
if not all(a.overlaps(b) for a, b in zip(want_intervals, got_values)):
|
255
|
+
raise ToleranceExceededError()
|
256
|
+
|
257
|
+
# Then check the part of the doctests without the numbers
|
258
|
+
# Continue the check process with floats replaced by stars
|
259
|
+
want = complex_regex.sub(complex_star_repl, want)
|
260
|
+
got = complex_regex.sub(complex_star_repl, got)
|
261
|
+
return [want, got]
|