passagemath-repl 10.4.62__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.
Files changed (162) hide show
  1. passagemath_repl-10.4.62.data/scripts/sage-cachegrind +25 -0
  2. passagemath_repl-10.4.62.data/scripts/sage-callgrind +16 -0
  3. passagemath_repl-10.4.62.data/scripts/sage-cleaner +230 -0
  4. passagemath_repl-10.4.62.data/scripts/sage-coverage +327 -0
  5. passagemath_repl-10.4.62.data/scripts/sage-eval +14 -0
  6. passagemath_repl-10.4.62.data/scripts/sage-fixdoctests +708 -0
  7. passagemath_repl-10.4.62.data/scripts/sage-inline-fortran +12 -0
  8. passagemath_repl-10.4.62.data/scripts/sage-ipynb2rst +50 -0
  9. passagemath_repl-10.4.62.data/scripts/sage-ipython +16 -0
  10. passagemath_repl-10.4.62.data/scripts/sage-massif +25 -0
  11. passagemath_repl-10.4.62.data/scripts/sage-notebook +267 -0
  12. passagemath_repl-10.4.62.data/scripts/sage-omega +25 -0
  13. passagemath_repl-10.4.62.data/scripts/sage-preparse +302 -0
  14. passagemath_repl-10.4.62.data/scripts/sage-run +27 -0
  15. passagemath_repl-10.4.62.data/scripts/sage-run-cython +10 -0
  16. passagemath_repl-10.4.62.data/scripts/sage-runtests +9 -0
  17. passagemath_repl-10.4.62.data/scripts/sage-startuptime.py +163 -0
  18. passagemath_repl-10.4.62.data/scripts/sage-valgrind +34 -0
  19. passagemath_repl-10.4.62.dist-info/METADATA +77 -0
  20. passagemath_repl-10.4.62.dist-info/RECORD +162 -0
  21. passagemath_repl-10.4.62.dist-info/WHEEL +5 -0
  22. passagemath_repl-10.4.62.dist-info/top_level.txt +1 -0
  23. sage/all__sagemath_repl.py +119 -0
  24. sage/doctest/__init__.py +4 -0
  25. sage/doctest/__main__.py +236 -0
  26. sage/doctest/all.py +4 -0
  27. sage/doctest/check_tolerance.py +261 -0
  28. sage/doctest/control.py +1727 -0
  29. sage/doctest/external.py +534 -0
  30. sage/doctest/fixtures.py +383 -0
  31. sage/doctest/forker.py +2665 -0
  32. sage/doctest/marked_output.py +102 -0
  33. sage/doctest/parsing.py +1708 -0
  34. sage/doctest/parsing_test.py +79 -0
  35. sage/doctest/reporting.py +733 -0
  36. sage/doctest/rif_tol.py +124 -0
  37. sage/doctest/sources.py +1657 -0
  38. sage/doctest/test.py +584 -0
  39. sage/doctest/tests/1second.rst +4 -0
  40. sage/doctest/tests/99seconds.rst +4 -0
  41. sage/doctest/tests/abort.rst +5 -0
  42. sage/doctest/tests/atexit.rst +7 -0
  43. sage/doctest/tests/fail_and_die.rst +6 -0
  44. sage/doctest/tests/initial.rst +15 -0
  45. sage/doctest/tests/interrupt.rst +7 -0
  46. sage/doctest/tests/interrupt_diehard.rst +14 -0
  47. sage/doctest/tests/keyboardinterrupt.rst +11 -0
  48. sage/doctest/tests/longtime.rst +5 -0
  49. sage/doctest/tests/nodoctest +5 -0
  50. sage/doctest/tests/random_seed.rst +4 -0
  51. sage/doctest/tests/show_skipped.rst +18 -0
  52. sage/doctest/tests/sig_on.rst +9 -0
  53. sage/doctest/tests/simple_failure.rst +8 -0
  54. sage/doctest/tests/sleep_and_raise.rst +106 -0
  55. sage/doctest/tests/tolerance.rst +31 -0
  56. sage/doctest/util.py +750 -0
  57. sage/interfaces/cleaner.py +48 -0
  58. sage/interfaces/quit.py +163 -0
  59. sage/misc/all__sagemath_repl.py +51 -0
  60. sage/misc/banner.py +235 -0
  61. sage/misc/benchmark.py +221 -0
  62. sage/misc/classgraph.py +131 -0
  63. sage/misc/copying.py +22 -0
  64. sage/misc/cython.py +694 -0
  65. sage/misc/dev_tools.py +745 -0
  66. sage/misc/edit_module.py +304 -0
  67. sage/misc/explain_pickle.py +3079 -0
  68. sage/misc/gperftools.py +361 -0
  69. sage/misc/inline_fortran.py +212 -0
  70. sage/misc/messaging.py +86 -0
  71. sage/misc/pager.py +21 -0
  72. sage/misc/profiler.py +179 -0
  73. sage/misc/python.py +70 -0
  74. sage/misc/remote_file.py +53 -0
  75. sage/misc/sage_eval.py +246 -0
  76. sage/misc/sage_input.py +3621 -0
  77. sage/misc/sagedoc.py +1742 -0
  78. sage/misc/sh.py +38 -0
  79. sage/misc/trace.py +90 -0
  80. sage/repl/__init__.py +16 -0
  81. sage/repl/all.py +15 -0
  82. sage/repl/attach.py +625 -0
  83. sage/repl/configuration.py +186 -0
  84. sage/repl/display/__init__.py +1 -0
  85. sage/repl/display/fancy_repr.py +354 -0
  86. sage/repl/display/formatter.py +318 -0
  87. sage/repl/display/jsmol_iframe.py +290 -0
  88. sage/repl/display/pretty_print.py +153 -0
  89. sage/repl/display/util.py +163 -0
  90. sage/repl/image.py +302 -0
  91. sage/repl/inputhook.py +91 -0
  92. sage/repl/interface_magic.py +298 -0
  93. sage/repl/interpreter.py +854 -0
  94. sage/repl/ipython_extension.py +593 -0
  95. sage/repl/ipython_kernel/__init__.py +1 -0
  96. sage/repl/ipython_kernel/__main__.py +4 -0
  97. sage/repl/ipython_kernel/all_jupyter.py +10 -0
  98. sage/repl/ipython_kernel/install.py +301 -0
  99. sage/repl/ipython_kernel/interact.py +278 -0
  100. sage/repl/ipython_kernel/kernel.py +217 -0
  101. sage/repl/ipython_kernel/widgets.py +466 -0
  102. sage/repl/ipython_kernel/widgets_sagenb.py +587 -0
  103. sage/repl/ipython_tests.py +163 -0
  104. sage/repl/load.py +326 -0
  105. sage/repl/preparse.py +2218 -0
  106. sage/repl/prompts.py +90 -0
  107. sage/repl/rich_output/__init__.py +4 -0
  108. sage/repl/rich_output/backend_base.py +648 -0
  109. sage/repl/rich_output/backend_doctest.py +316 -0
  110. sage/repl/rich_output/backend_emacs.py +151 -0
  111. sage/repl/rich_output/backend_ipython.py +596 -0
  112. sage/repl/rich_output/buffer.py +311 -0
  113. sage/repl/rich_output/display_manager.py +829 -0
  114. sage/repl/rich_output/example.avi +0 -0
  115. sage/repl/rich_output/example.canvas3d +1 -0
  116. sage/repl/rich_output/example.dvi +0 -0
  117. sage/repl/rich_output/example.flv +0 -0
  118. sage/repl/rich_output/example.gif +0 -0
  119. sage/repl/rich_output/example.jpg +0 -0
  120. sage/repl/rich_output/example.mkv +0 -0
  121. sage/repl/rich_output/example.mov +0 -0
  122. sage/repl/rich_output/example.mp4 +0 -0
  123. sage/repl/rich_output/example.ogv +0 -0
  124. sage/repl/rich_output/example.pdf +0 -0
  125. sage/repl/rich_output/example.png +0 -0
  126. sage/repl/rich_output/example.svg +54 -0
  127. sage/repl/rich_output/example.webm +0 -0
  128. sage/repl/rich_output/example.wmv +0 -0
  129. sage/repl/rich_output/example_jmol.spt.zip +0 -0
  130. sage/repl/rich_output/example_wavefront_scene.mtl +7 -0
  131. sage/repl/rich_output/example_wavefront_scene.obj +17 -0
  132. sage/repl/rich_output/output_basic.py +391 -0
  133. sage/repl/rich_output/output_browser.py +103 -0
  134. sage/repl/rich_output/output_catalog.py +54 -0
  135. sage/repl/rich_output/output_graphics.py +320 -0
  136. sage/repl/rich_output/output_graphics3d.py +345 -0
  137. sage/repl/rich_output/output_video.py +231 -0
  138. sage/repl/rich_output/preferences.py +432 -0
  139. sage/repl/rich_output/pretty_print.py +339 -0
  140. sage/repl/rich_output/test_backend.py +201 -0
  141. sage/repl/user_globals.py +214 -0
  142. sage/tests/__init__.py +1 -0
  143. sage/tests/all.py +3 -0
  144. sage/tests/article_heuberger_krenn_kropf_fsm-in-sage.py +630 -0
  145. sage/tests/arxiv_0812_2725.py +351 -0
  146. sage/tests/benchmark.py +1923 -0
  147. sage/tests/book_schilling_zabrocki_kschur_primer.py +795 -0
  148. sage/tests/book_stein_ent.py +651 -0
  149. sage/tests/book_stein_modform.py +558 -0
  150. sage/tests/cmdline.py +790 -0
  151. sage/tests/combinatorial_hopf_algebras.py +52 -0
  152. sage/tests/finite_poset.py +623 -0
  153. sage/tests/functools_partial_src.py +27 -0
  154. sage/tests/gosper-sum.py +218 -0
  155. sage/tests/lazy_imports.py +28 -0
  156. sage/tests/modular_group_cohomology.py +80 -0
  157. sage/tests/numpy.py +21 -0
  158. sage/tests/parigp.py +76 -0
  159. sage/tests/startup.py +27 -0
  160. sage/tests/symbolic-series.py +76 -0
  161. sage/tests/sympy.py +16 -0
  162. sage/tests/test_deprecation.py +31 -0
@@ -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,4 @@
1
+ # sage_setup: distribution = sagemath-repl
2
+ from sage.misc.lazy_import import lazy_import
3
+ lazy_import('sage.doctest.control', 'run_doctests')
4
+ del lazy_import
@@ -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]