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/util.py
ADDED
@@ -0,0 +1,750 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-repl
|
2
|
+
"""
|
3
|
+
Utility functions
|
4
|
+
|
5
|
+
This module contains various utility functions and classes used in doctesting.
|
6
|
+
|
7
|
+
AUTHORS:
|
8
|
+
|
9
|
+
- David Roe (2012-03-27) -- initial version, based on Robert Bradshaw's code.
|
10
|
+
"""
|
11
|
+
|
12
|
+
# ****************************************************************************
|
13
|
+
# Copyright (C) 2012 David Roe <roed.math@gmail.com>
|
14
|
+
# 2012 Robert Bradshaw <robertwb@gmail.com>
|
15
|
+
# 2012 William Stein <wstein@gmail.com>
|
16
|
+
# 2013 Jeroen Demeyer <jdemeyer@cage.ugent.be>
|
17
|
+
# 2014 Volker Braun
|
18
|
+
# 2017 Frédéric Chapoton
|
19
|
+
#
|
20
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
21
|
+
# as published by the Free Software Foundation; either version 2 of
|
22
|
+
# the License, or (at your option) any later version.
|
23
|
+
# https://www.gnu.org/licenses/
|
24
|
+
# ****************************************************************************
|
25
|
+
|
26
|
+
from time import time as walltime
|
27
|
+
from os import sysconf, times
|
28
|
+
|
29
|
+
def count_noun(number, noun, plural=None, pad_number=False, pad_noun=False):
|
30
|
+
"""
|
31
|
+
EXAMPLES::
|
32
|
+
|
33
|
+
sage: from sage.doctest.util import count_noun
|
34
|
+
sage: count_noun(1, "apple")
|
35
|
+
'1 apple'
|
36
|
+
sage: count_noun(1, "apple", pad_noun=True)
|
37
|
+
'1 apple '
|
38
|
+
sage: count_noun(1, "apple", pad_number=3)
|
39
|
+
' 1 apple'
|
40
|
+
sage: count_noun(2, "orange")
|
41
|
+
'2 oranges'
|
42
|
+
sage: count_noun(3, "peach", "peaches")
|
43
|
+
'3 peaches'
|
44
|
+
sage: count_noun(1, "peach", plural='peaches', pad_noun=True)
|
45
|
+
'1 peach '
|
46
|
+
"""
|
47
|
+
if plural is None:
|
48
|
+
plural = noun + "s"
|
49
|
+
if pad_noun:
|
50
|
+
# We assume that the plural is never shorter than the noun....
|
51
|
+
pad_noun = " " * (len(plural) - len(noun))
|
52
|
+
else:
|
53
|
+
pad_noun = ""
|
54
|
+
if pad_number:
|
55
|
+
number_str = ("%%%sd" % pad_number) % number
|
56
|
+
else:
|
57
|
+
number_str = "%d" % number
|
58
|
+
if number == 1:
|
59
|
+
return "%s %s%s" % (number_str, noun, pad_noun)
|
60
|
+
else:
|
61
|
+
return "%s %s" % (number_str, plural)
|
62
|
+
|
63
|
+
|
64
|
+
def dict_difference(self, other):
|
65
|
+
"""
|
66
|
+
Return a dict with all key-value pairs occurring in ``self`` but not
|
67
|
+
in ``other``.
|
68
|
+
|
69
|
+
EXAMPLES::
|
70
|
+
|
71
|
+
sage: from sage.doctest.util import dict_difference
|
72
|
+
sage: d1 = {1: 'a', 2: 'b', 3: 'c'}
|
73
|
+
sage: d2 = {1: 'a', 2: 'x', 4: 'c'}
|
74
|
+
sage: dict_difference(d2, d1)
|
75
|
+
{2: 'x', 4: 'c'}
|
76
|
+
|
77
|
+
::
|
78
|
+
|
79
|
+
sage: from sage.doctest.control import DocTestDefaults
|
80
|
+
sage: D1 = DocTestDefaults()
|
81
|
+
sage: D2 = DocTestDefaults(foobar='hello', timeout=100)
|
82
|
+
sage: dict_difference(D2.__dict__, D1.__dict__)
|
83
|
+
{'foobar': 'hello', 'timeout': 100}
|
84
|
+
"""
|
85
|
+
D = {}
|
86
|
+
for k, v in self.items():
|
87
|
+
try:
|
88
|
+
if other[k] == v:
|
89
|
+
continue
|
90
|
+
except KeyError:
|
91
|
+
pass
|
92
|
+
D[k] = v
|
93
|
+
return D
|
94
|
+
|
95
|
+
|
96
|
+
class Timer:
|
97
|
+
"""
|
98
|
+
A simple timer.
|
99
|
+
|
100
|
+
EXAMPLES::
|
101
|
+
|
102
|
+
sage: from sage.doctest.util import Timer
|
103
|
+
sage: Timer()
|
104
|
+
{}
|
105
|
+
sage: TestSuite(Timer()).run()
|
106
|
+
"""
|
107
|
+
|
108
|
+
def _proc_stat_cpu_seconds(self, path):
|
109
|
+
r"""
|
110
|
+
Parse a "stat" file from the ``/proc`` filesystem to get
|
111
|
+
the cputime of a process.
|
112
|
+
|
113
|
+
This also includes the times for child processes, but only
|
114
|
+
those that have already terminated and for which ``wait()``
|
115
|
+
was called. It is important to note that pexpect processes DO
|
116
|
+
NOT fall into that category.
|
117
|
+
|
118
|
+
The document ``Documentation/filesystems/proc.rst`` within the
|
119
|
+
Linux kernel source tree defines a "stat" file.
|
120
|
+
|
121
|
+
INPUT:
|
122
|
+
|
123
|
+
- ``path`` -- string; the path to a "stat" file on the ``/proc``
|
124
|
+
filesystem, typically "/proc/<pid>/stat", from which we will
|
125
|
+
read cputime information
|
126
|
+
|
127
|
+
OUTPUT:
|
128
|
+
|
129
|
+
A nonnegative float representing the number of cpu-seconds
|
130
|
+
used by the process associated with ``path``. An ``OSError`` is
|
131
|
+
raised if anything goes wrong, which typically happens on
|
132
|
+
platforms that don't store this information under ``/proc``.
|
133
|
+
|
134
|
+
TESTS:
|
135
|
+
|
136
|
+
About all we can say for certain is that this will return a
|
137
|
+
nonnegative float or raise an ``OSError``::
|
138
|
+
|
139
|
+
sage: from sage.doctest.util import Timer
|
140
|
+
sage: cputime = float(0.0)
|
141
|
+
sage: path = "/proc/1/stat"
|
142
|
+
sage: try:
|
143
|
+
....: cputime = Timer()._proc_stat_cpu_seconds(path)
|
144
|
+
....: except OSError:
|
145
|
+
....: pass
|
146
|
+
sage: cputime >= 0.0
|
147
|
+
True
|
148
|
+
sage: isinstance(cputime, float)
|
149
|
+
True
|
150
|
+
|
151
|
+
We can force an ``OSError`` with an invalid PID::
|
152
|
+
|
153
|
+
sage: from sage.doctest.util import Timer
|
154
|
+
sage: path = "/proc/-1/stat"
|
155
|
+
sage: cputime = Timer()._proc_stat_cpu_seconds(path)
|
156
|
+
Traceback (most recent call last):
|
157
|
+
...
|
158
|
+
OSError: unable to access /proc/-1/stat
|
159
|
+
|
160
|
+
Or with an unparseable file (wrong number of fields, non-float
|
161
|
+
fields, et cetera)::
|
162
|
+
|
163
|
+
sage: from tempfile import NamedTemporaryFile
|
164
|
+
sage: from os import unlink
|
165
|
+
sage: from sage.doctest.util import Timer
|
166
|
+
sage: with NamedTemporaryFile(delete=False, mode="w") as f:
|
167
|
+
....: _ = f.write("1 2 3 4 5")
|
168
|
+
sage: cputime = Timer()._proc_stat_cpu_seconds(f.name)
|
169
|
+
Traceback (most recent call last):
|
170
|
+
...
|
171
|
+
OSError: unable to parse ...
|
172
|
+
sage: os.unlink(f.name)
|
173
|
+
sage: with NamedTemporaryFile(delete=False, mode="w") as f:
|
174
|
+
....: _ = f.write("1 2 3 4 5 6 7 8 9 10 11 12 w x y z 17")
|
175
|
+
sage: cputime = Timer()._proc_stat_cpu_seconds(f.name)
|
176
|
+
Traceback (most recent call last):
|
177
|
+
...
|
178
|
+
OSError: unable to parse ...
|
179
|
+
sage: os.unlink(f.name)
|
180
|
+
|
181
|
+
"""
|
182
|
+
try:
|
183
|
+
with open(path, "r") as statfile:
|
184
|
+
stats = statfile.read().split()
|
185
|
+
except (FileNotFoundError, PermissionError) as e:
|
186
|
+
# FileNotFoundError: bad PID, or no /proc support
|
187
|
+
# PermissionError: can't read the stat file
|
188
|
+
raise OSError(f"unable to access {path}") from e
|
189
|
+
|
190
|
+
if len(stats) < 17:
|
191
|
+
raise OSError(f"unable to parse {path}")
|
192
|
+
|
193
|
+
try:
|
194
|
+
# These fields used to be documented in the proc(5) man
|
195
|
+
# page, but are now most easily found in the Linux kernel
|
196
|
+
# documentation (Documentation/filesystems/proc.rst). The
|
197
|
+
# intent is to sum the user- and kernel-mode "jiffies" for
|
198
|
+
# both the given process and its children.
|
199
|
+
cputicks = sum( float(s) for s in stats[13:17] )
|
200
|
+
except (ArithmeticError, TypeError, ValueError) as e:
|
201
|
+
# ArithmeticError: unexpected (non-numeric?) values in fields
|
202
|
+
# TypeError/ValueError: fields can't be converted to float
|
203
|
+
raise OSError(f"unable to parse {path}") from e
|
204
|
+
|
205
|
+
try:
|
206
|
+
hertz = sysconf("SC_CLK_TCK")
|
207
|
+
except (ValueError) as e:
|
208
|
+
# ValueError: SC_CLK_TCK doesn't exist
|
209
|
+
raise OSError("SC_CLK_TCK sysconf not found") from e
|
210
|
+
|
211
|
+
if hertz <= 0:
|
212
|
+
# The python documentation for os.sysconf() says, "If the
|
213
|
+
# configuration value specified by name isn’t defined, -1
|
214
|
+
# is returned." Having tried this with a junk value, I
|
215
|
+
# don't believe it: I got a ValueError that was handled
|
216
|
+
# above. Nevertheless, we play it safe here and turn a -1
|
217
|
+
# into an OSError. We check for zero, too, because we're
|
218
|
+
# about to divide by it.
|
219
|
+
raise OSError("SC_CLK_TCK sysconf is nonpositive")
|
220
|
+
|
221
|
+
return (cputicks / hertz)
|
222
|
+
|
223
|
+
def _quick_cputime(self, expect_objects):
|
224
|
+
r"""
|
225
|
+
A fast replacement for ``sage.misc.timing.cputime``.
|
226
|
+
|
227
|
+
This is a "reliable" replacement (on Linux/BSD) that takes
|
228
|
+
subprocesses (particularly pexpect interfaces) into
|
229
|
+
account. The ``cputime()`` function from the ``misc`` module
|
230
|
+
can be passed ``subprocesses=True``, but this has a few
|
231
|
+
faults; mainly that it relies on each pexpect interface to
|
232
|
+
implement its own ``cputime()`` function. And most of our
|
233
|
+
pexpect interfaces either don't implement one, or implement
|
234
|
+
one in a way that requires the subprocess (being pexpected) to
|
235
|
+
be in perfect working condition -- that will often not be the
|
236
|
+
case at the end of a doctest line.
|
237
|
+
|
238
|
+
INPUT:
|
239
|
+
|
240
|
+
- ``expect_objects`` -- list; a list of
|
241
|
+
:class:`sage.interfaces.expect.Expect` instances whose CPU
|
242
|
+
times will be included in the total
|
243
|
+
|
244
|
+
OUTPUT:
|
245
|
+
|
246
|
+
A float measuring the cputime in seconds of the sage process
|
247
|
+
and all its subprocesses.
|
248
|
+
|
249
|
+
TESTS:
|
250
|
+
|
251
|
+
About all we can say for certain is that this will return a
|
252
|
+
nonnegative float::
|
253
|
+
|
254
|
+
sage: from sage.doctest.util import Timer
|
255
|
+
sage: from sage.interfaces.quit import expect_objects
|
256
|
+
sage: cputime = Timer()._quick_cputime(expect_objects)
|
257
|
+
sage: cputime >= 0.0
|
258
|
+
True
|
259
|
+
sage: isinstance(cputime, float)
|
260
|
+
True
|
261
|
+
|
262
|
+
If an error occurs in :meth:`_proc_stat_cpu_seconds`, this
|
263
|
+
function should still return a valid answer, albeit one that
|
264
|
+
is missing timing information for the PID that failed::
|
265
|
+
|
266
|
+
sage: class FakeExpect:
|
267
|
+
....: def __call__(self):
|
268
|
+
....: return self
|
269
|
+
....: def is_running(self):
|
270
|
+
....: return True
|
271
|
+
....: def pid(self):
|
272
|
+
....: return -1
|
273
|
+
sage: e = FakeExpect()
|
274
|
+
sage: from sage.doctest.util import Timer
|
275
|
+
sage: cputime = Timer()._quick_cputime([e])
|
276
|
+
sage: cputime >= 0.0
|
277
|
+
True
|
278
|
+
sage: isinstance(cputime, float)
|
279
|
+
True
|
280
|
+
"""
|
281
|
+
# Start by using os.times() to get the cputime for sage itself
|
282
|
+
# and any subprocesses that have been wait()ed for and that
|
283
|
+
# have terminated.
|
284
|
+
cputime = sum( times()[:4] )
|
285
|
+
|
286
|
+
# Now try to get the times for any pexpect interfaces, since
|
287
|
+
# they do not fall into the category above.
|
288
|
+
for s in expect_objects:
|
289
|
+
S = s()
|
290
|
+
if S and S.is_running():
|
291
|
+
try:
|
292
|
+
# This will fail anywhere but linux/BSD, but
|
293
|
+
# there's no good cross-platform way to get the
|
294
|
+
# cputimes from pexpect interfaces without totally
|
295
|
+
# mucking up the doctests.
|
296
|
+
path = f"/proc/{S.pid()}/stat"
|
297
|
+
cputime += self._proc_stat_cpu_seconds(path)
|
298
|
+
except OSError:
|
299
|
+
# If we're on macOS, we can fall back to using
|
300
|
+
# psutil, but only if it's installed. It's usually
|
301
|
+
# installed as a transitive dependency (ipython
|
302
|
+
# needs it), but it isn't explicitly listed as
|
303
|
+
# a dependency of sagelib.
|
304
|
+
try:
|
305
|
+
from psutil import (NoSuchProcess,
|
306
|
+
Process,
|
307
|
+
ZombieProcess)
|
308
|
+
try:
|
309
|
+
cputime += sum(Process(S.pid()).cpu_times()[0:2])
|
310
|
+
except (ValueError, NoSuchProcess, ZombieProcess):
|
311
|
+
# ValueError: invalid (e.g. negative) PID
|
312
|
+
# NoSuchProcess: it's gone
|
313
|
+
# ZombieProcess: PID refers to a zombie
|
314
|
+
pass
|
315
|
+
except ImportError:
|
316
|
+
pass
|
317
|
+
|
318
|
+
return cputime
|
319
|
+
|
320
|
+
def start(self):
|
321
|
+
"""
|
322
|
+
Start the timer.
|
323
|
+
|
324
|
+
Can be called multiple times to reset the timer.
|
325
|
+
|
326
|
+
EXAMPLES::
|
327
|
+
|
328
|
+
sage: from sage.doctest.util import Timer
|
329
|
+
sage: Timer().start()
|
330
|
+
{'cputime': ..., 'walltime': ...}
|
331
|
+
"""
|
332
|
+
from sage.interfaces.quit import expect_objects
|
333
|
+
self.cputime = self._quick_cputime(expect_objects)
|
334
|
+
self.walltime = walltime()
|
335
|
+
return self
|
336
|
+
|
337
|
+
def stop(self):
|
338
|
+
"""
|
339
|
+
Stops the timer, recording the time that has passed since it
|
340
|
+
was started.
|
341
|
+
|
342
|
+
EXAMPLES::
|
343
|
+
|
344
|
+
sage: from sage.doctest.util import Timer
|
345
|
+
sage: import time
|
346
|
+
sage: timer = Timer().start()
|
347
|
+
sage: time.sleep(float(0.5))
|
348
|
+
sage: timer.stop()
|
349
|
+
{'cputime': ..., 'walltime': ...}
|
350
|
+
"""
|
351
|
+
from sage.interfaces.quit import expect_objects
|
352
|
+
self.cputime = self._quick_cputime(expect_objects) - self.cputime
|
353
|
+
self.walltime = walltime() - self.walltime
|
354
|
+
return self
|
355
|
+
|
356
|
+
def annotate(self, object):
|
357
|
+
"""
|
358
|
+
Annotates the given object with the cputime and walltime
|
359
|
+
stored in this timer.
|
360
|
+
|
361
|
+
EXAMPLES::
|
362
|
+
|
363
|
+
sage: # needs sage.schemes
|
364
|
+
sage: from sage.doctest.util import Timer
|
365
|
+
sage: Timer().start().annotate(EllipticCurve) # needs sage.schemes
|
366
|
+
sage: EllipticCurve.cputime # random # needs sage.schemes
|
367
|
+
2.817255
|
368
|
+
sage: EllipticCurve.walltime # random # needs sage.schemes
|
369
|
+
1332649288.410404
|
370
|
+
"""
|
371
|
+
object.cputime = self.cputime
|
372
|
+
object.walltime = self.walltime
|
373
|
+
|
374
|
+
def __repr__(self):
|
375
|
+
"""
|
376
|
+
String representation is from the dictionary.
|
377
|
+
|
378
|
+
EXAMPLES::
|
379
|
+
|
380
|
+
sage: from sage.doctest.util import Timer
|
381
|
+
sage: repr(Timer().start()) # indirect doctest
|
382
|
+
"{'cputime': ..., 'walltime': ...}"
|
383
|
+
"""
|
384
|
+
return str(self)
|
385
|
+
|
386
|
+
def __str__(self):
|
387
|
+
"""
|
388
|
+
String representation is from the dictionary.
|
389
|
+
|
390
|
+
EXAMPLES::
|
391
|
+
|
392
|
+
sage: from sage.doctest.util import Timer
|
393
|
+
sage: str(Timer().start()) # indirect doctest
|
394
|
+
"{'cputime': ..., 'walltime': ...}"
|
395
|
+
"""
|
396
|
+
return str(self.__dict__)
|
397
|
+
|
398
|
+
def __eq__(self, other):
|
399
|
+
"""
|
400
|
+
Comparison.
|
401
|
+
|
402
|
+
EXAMPLES::
|
403
|
+
|
404
|
+
sage: from sage.doctest.util import Timer
|
405
|
+
sage: Timer() == Timer()
|
406
|
+
True
|
407
|
+
sage: t = Timer().start()
|
408
|
+
sage: loads(dumps(t)) == t
|
409
|
+
True
|
410
|
+
"""
|
411
|
+
if not isinstance(other, Timer):
|
412
|
+
return False
|
413
|
+
return self.__dict__ == other.__dict__
|
414
|
+
|
415
|
+
def __ne__(self, other):
|
416
|
+
"""
|
417
|
+
Test for non-equality.
|
418
|
+
|
419
|
+
EXAMPLES::
|
420
|
+
|
421
|
+
sage: from sage.doctest.util import Timer
|
422
|
+
sage: Timer() == Timer()
|
423
|
+
True
|
424
|
+
sage: t = Timer().start()
|
425
|
+
sage: loads(dumps(t)) != t
|
426
|
+
False
|
427
|
+
"""
|
428
|
+
return not (self == other)
|
429
|
+
|
430
|
+
|
431
|
+
# Inheritance rather then delegation as globals() must be a dict
|
432
|
+
class RecordingDict(dict):
|
433
|
+
"""
|
434
|
+
This dictionary is used for tracking the dependencies of an example.
|
435
|
+
|
436
|
+
This feature allows examples in different doctests to be grouped
|
437
|
+
for better timing data. It's obtained by recording whenever
|
438
|
+
anything is set or retrieved from this dictionary.
|
439
|
+
|
440
|
+
EXAMPLES::
|
441
|
+
|
442
|
+
sage: from sage.doctest.util import RecordingDict
|
443
|
+
sage: D = RecordingDict(test=17)
|
444
|
+
sage: D.got
|
445
|
+
set()
|
446
|
+
sage: D['test']
|
447
|
+
17
|
448
|
+
sage: D.got
|
449
|
+
{'test'}
|
450
|
+
sage: D.set
|
451
|
+
set()
|
452
|
+
sage: D['a'] = 1
|
453
|
+
sage: D['a']
|
454
|
+
1
|
455
|
+
sage: D.set
|
456
|
+
{'a'}
|
457
|
+
sage: D.got
|
458
|
+
{'test'}
|
459
|
+
|
460
|
+
TESTS::
|
461
|
+
|
462
|
+
sage: TestSuite(D).run()
|
463
|
+
"""
|
464
|
+
def __init__(self, *args, **kwds):
|
465
|
+
"""
|
466
|
+
Initialization arguments are the same as for a normal dictionary.
|
467
|
+
|
468
|
+
EXAMPLES::
|
469
|
+
|
470
|
+
sage: from sage.doctest.util import RecordingDict
|
471
|
+
sage: D = RecordingDict(d = 42)
|
472
|
+
sage: D.got
|
473
|
+
set()
|
474
|
+
"""
|
475
|
+
dict.__init__(self, *args, **kwds)
|
476
|
+
self.start()
|
477
|
+
|
478
|
+
def start(self):
|
479
|
+
"""
|
480
|
+
We track which variables have been set or retrieved.
|
481
|
+
This function initializes these lists to be empty.
|
482
|
+
|
483
|
+
EXAMPLES::
|
484
|
+
|
485
|
+
sage: from sage.doctest.util import RecordingDict
|
486
|
+
sage: D = RecordingDict(d = 42)
|
487
|
+
sage: D.set
|
488
|
+
set()
|
489
|
+
sage: D['a'] = 4
|
490
|
+
sage: D.set
|
491
|
+
{'a'}
|
492
|
+
sage: D.start(); D.set
|
493
|
+
set()
|
494
|
+
"""
|
495
|
+
self.set = set()
|
496
|
+
self.got = set()
|
497
|
+
|
498
|
+
def __getitem__(self, name):
|
499
|
+
"""
|
500
|
+
EXAMPLES::
|
501
|
+
|
502
|
+
sage: from sage.doctest.util import RecordingDict
|
503
|
+
sage: D = RecordingDict(d = 42)
|
504
|
+
sage: D['a'] = 4
|
505
|
+
sage: D.got
|
506
|
+
set()
|
507
|
+
sage: D['a'] # indirect doctest
|
508
|
+
4
|
509
|
+
sage: D.got
|
510
|
+
set()
|
511
|
+
sage: D['d']
|
512
|
+
42
|
513
|
+
sage: D.got
|
514
|
+
{'d'}
|
515
|
+
"""
|
516
|
+
if name not in self.set:
|
517
|
+
self.got.add(name)
|
518
|
+
return dict.__getitem__(self, name)
|
519
|
+
|
520
|
+
def __setitem__(self, name, value):
|
521
|
+
"""
|
522
|
+
EXAMPLES::
|
523
|
+
|
524
|
+
sage: from sage.doctest.util import RecordingDict
|
525
|
+
sage: D = RecordingDict(d = 42)
|
526
|
+
sage: D['a'] = 4 # indirect doctest
|
527
|
+
sage: D.set
|
528
|
+
{'a'}
|
529
|
+
"""
|
530
|
+
self.set.add(name)
|
531
|
+
dict.__setitem__(self, name, value)
|
532
|
+
|
533
|
+
def __delitem__(self, name):
|
534
|
+
"""
|
535
|
+
EXAMPLES::
|
536
|
+
|
537
|
+
sage: from sage.doctest.util import RecordingDict
|
538
|
+
sage: D = RecordingDict(d = 42)
|
539
|
+
sage: del D['d'] # indirect doctest
|
540
|
+
sage: D.set
|
541
|
+
{'d'}
|
542
|
+
"""
|
543
|
+
self.set.add(name)
|
544
|
+
dict.__delitem__(self, name)
|
545
|
+
|
546
|
+
def get(self, name, default=None):
|
547
|
+
"""
|
548
|
+
EXAMPLES::
|
549
|
+
|
550
|
+
sage: from sage.doctest.util import RecordingDict
|
551
|
+
sage: D = RecordingDict(d = 42)
|
552
|
+
sage: D.get('d')
|
553
|
+
42
|
554
|
+
sage: D.got
|
555
|
+
{'d'}
|
556
|
+
sage: D.get('not_here')
|
557
|
+
sage: sorted(list(D.got))
|
558
|
+
['d', 'not_here']
|
559
|
+
"""
|
560
|
+
if name not in self.set:
|
561
|
+
self.got.add(name)
|
562
|
+
return dict.get(self, name, default)
|
563
|
+
|
564
|
+
def copy(self):
|
565
|
+
"""
|
566
|
+
Note that set and got are not copied.
|
567
|
+
|
568
|
+
EXAMPLES::
|
569
|
+
|
570
|
+
sage: from sage.doctest.util import RecordingDict
|
571
|
+
sage: D = RecordingDict(d = 42)
|
572
|
+
sage: D['a'] = 4
|
573
|
+
sage: D.set
|
574
|
+
{'a'}
|
575
|
+
sage: E = D.copy()
|
576
|
+
sage: E.set
|
577
|
+
set()
|
578
|
+
sage: sorted(E.keys())
|
579
|
+
['a', 'd']
|
580
|
+
"""
|
581
|
+
return RecordingDict(dict.copy(self))
|
582
|
+
|
583
|
+
def __reduce__(self):
|
584
|
+
"""
|
585
|
+
Pickling.
|
586
|
+
|
587
|
+
EXAMPLES::
|
588
|
+
|
589
|
+
sage: from sage.doctest.util import RecordingDict
|
590
|
+
sage: D = RecordingDict(d = 42)
|
591
|
+
sage: D['a'] = 4
|
592
|
+
sage: D.get('not_here')
|
593
|
+
sage: E = loads(dumps(D))
|
594
|
+
sage: E.got
|
595
|
+
{'not_here'}
|
596
|
+
"""
|
597
|
+
return make_recording_dict, (dict(self), self.set, self.got)
|
598
|
+
|
599
|
+
|
600
|
+
def make_recording_dict(D, st, gt):
|
601
|
+
"""
|
602
|
+
Auxiliary function for pickling.
|
603
|
+
|
604
|
+
EXAMPLES::
|
605
|
+
|
606
|
+
sage: from sage.doctest.util import make_recording_dict
|
607
|
+
sage: D = make_recording_dict({'a':4,'d':42},set([]),set(['not_here']))
|
608
|
+
sage: sorted(D.items())
|
609
|
+
[('a', 4), ('d', 42)]
|
610
|
+
sage: D.got
|
611
|
+
{'not_here'}
|
612
|
+
"""
|
613
|
+
ans = RecordingDict(D)
|
614
|
+
ans.set = st
|
615
|
+
ans.got = gt
|
616
|
+
return ans
|
617
|
+
|
618
|
+
class NestedName:
|
619
|
+
"""
|
620
|
+
Class used to construct fully qualified names based on indentation level.
|
621
|
+
|
622
|
+
EXAMPLES::
|
623
|
+
|
624
|
+
sage: from sage.doctest.util import NestedName
|
625
|
+
sage: qname = NestedName('sage.categories.algebras')
|
626
|
+
sage: qname[0] = 'Algebras'; qname
|
627
|
+
sage.categories.algebras.Algebras
|
628
|
+
sage: qname[4] = '__contains__'; qname
|
629
|
+
sage.categories.algebras.Algebras.__contains__
|
630
|
+
sage: qname[4] = 'ParentMethods'
|
631
|
+
sage: qname[8] = 'from_base_ring'; qname
|
632
|
+
sage.categories.algebras.Algebras.ParentMethods.from_base_ring
|
633
|
+
|
634
|
+
TESTS::
|
635
|
+
|
636
|
+
sage: TestSuite(qname).run()
|
637
|
+
"""
|
638
|
+
def __init__(self, base):
|
639
|
+
"""
|
640
|
+
INPUT:
|
641
|
+
|
642
|
+
- ``base`` -- string; the name of the module
|
643
|
+
|
644
|
+
EXAMPLES::
|
645
|
+
|
646
|
+
sage: from sage.doctest.util import NestedName
|
647
|
+
sage: qname = NestedName('sage.categories.algebras')
|
648
|
+
sage: qname
|
649
|
+
sage.categories.algebras
|
650
|
+
"""
|
651
|
+
self.all = [base]
|
652
|
+
|
653
|
+
def __setitem__(self, index, value):
|
654
|
+
"""
|
655
|
+
Set the value at a given indentation level.
|
656
|
+
|
657
|
+
INPUT:
|
658
|
+
|
659
|
+
- ``index`` -- positive integer; the indentation level (often a
|
660
|
+
multiple of 4, but not necessarily)
|
661
|
+
- ``value`` -- string; the name of the class or function at that
|
662
|
+
indentation level
|
663
|
+
|
664
|
+
EXAMPLES::
|
665
|
+
|
666
|
+
sage: from sage.doctest.util import NestedName
|
667
|
+
sage: qname = NestedName('sage.categories.algebras')
|
668
|
+
sage: qname[1] = 'Algebras' # indirect doctest
|
669
|
+
sage: qname
|
670
|
+
sage.categories.algebras.Algebras
|
671
|
+
sage: qname.all
|
672
|
+
['sage.categories.algebras', None, 'Algebras']
|
673
|
+
"""
|
674
|
+
if index < 0:
|
675
|
+
raise ValueError
|
676
|
+
while len(self.all) <= index:
|
677
|
+
self.all.append(None)
|
678
|
+
self.all[index+1:] = [value]
|
679
|
+
|
680
|
+
def __str__(self):
|
681
|
+
"""
|
682
|
+
Return a .-separated string giving the full name.
|
683
|
+
|
684
|
+
EXAMPLES::
|
685
|
+
|
686
|
+
sage: from sage.doctest.util import NestedName
|
687
|
+
sage: qname = NestedName('sage.categories.algebras')
|
688
|
+
sage: qname[1] = 'Algebras'
|
689
|
+
sage: qname[44] = 'at_the_end_of_the_universe'
|
690
|
+
sage: str(qname) # indirect doctest
|
691
|
+
'sage.categories.algebras.Algebras.at_the_end_of_the_universe'
|
692
|
+
"""
|
693
|
+
return repr(self)
|
694
|
+
|
695
|
+
def __repr__(self):
|
696
|
+
"""
|
697
|
+
Return a .-separated string giving the full name.
|
698
|
+
|
699
|
+
EXAMPLES::
|
700
|
+
|
701
|
+
sage: from sage.doctest.util import NestedName
|
702
|
+
sage: qname = NestedName('sage.categories.algebras')
|
703
|
+
sage: qname[1] = 'Algebras'
|
704
|
+
sage: qname[44] = 'at_the_end_of_the_universe'
|
705
|
+
sage: print(qname) # indirect doctest
|
706
|
+
sage.categories.algebras.Algebras.at_the_end_of_the_universe
|
707
|
+
"""
|
708
|
+
return '.'.join(a for a in self.all if a is not None)
|
709
|
+
|
710
|
+
def __eq__(self, other):
|
711
|
+
"""
|
712
|
+
Comparison is just comparison of the underlying lists.
|
713
|
+
|
714
|
+
EXAMPLES::
|
715
|
+
|
716
|
+
sage: from sage.doctest.util import NestedName
|
717
|
+
sage: qname = NestedName('sage.categories.algebras')
|
718
|
+
sage: qname2 = NestedName('sage.categories.algebras')
|
719
|
+
sage: qname == qname2
|
720
|
+
True
|
721
|
+
sage: qname[0] = 'Algebras'
|
722
|
+
sage: qname2[2] = 'Algebras'
|
723
|
+
sage: repr(qname) == repr(qname2)
|
724
|
+
True
|
725
|
+
sage: qname == qname2
|
726
|
+
False
|
727
|
+
"""
|
728
|
+
if not isinstance(other, NestedName):
|
729
|
+
return False
|
730
|
+
return self.all == other.all
|
731
|
+
|
732
|
+
def __ne__(self, other):
|
733
|
+
"""
|
734
|
+
Test for non-equality.
|
735
|
+
|
736
|
+
EXAMPLES::
|
737
|
+
|
738
|
+
sage: from sage.doctest.util import NestedName
|
739
|
+
sage: qname = NestedName('sage.categories.algebras')
|
740
|
+
sage: qname2 = NestedName('sage.categories.algebras')
|
741
|
+
sage: qname != qname2
|
742
|
+
False
|
743
|
+
sage: qname[0] = 'Algebras'
|
744
|
+
sage: qname2[2] = 'Algebras'
|
745
|
+
sage: repr(qname) == repr(qname2)
|
746
|
+
True
|
747
|
+
sage: qname != qname2
|
748
|
+
True
|
749
|
+
"""
|
750
|
+
return not (self == other)
|