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.
Files changed (162) hide show
  1. passagemath_repl-10.5.1.data/scripts/sage-cachegrind +25 -0
  2. passagemath_repl-10.5.1.data/scripts/sage-callgrind +16 -0
  3. passagemath_repl-10.5.1.data/scripts/sage-cleaner +230 -0
  4. passagemath_repl-10.5.1.data/scripts/sage-coverage +327 -0
  5. passagemath_repl-10.5.1.data/scripts/sage-eval +14 -0
  6. passagemath_repl-10.5.1.data/scripts/sage-fixdoctests +710 -0
  7. passagemath_repl-10.5.1.data/scripts/sage-inline-fortran +12 -0
  8. passagemath_repl-10.5.1.data/scripts/sage-ipynb2rst +50 -0
  9. passagemath_repl-10.5.1.data/scripts/sage-ipython +16 -0
  10. passagemath_repl-10.5.1.data/scripts/sage-massif +25 -0
  11. passagemath_repl-10.5.1.data/scripts/sage-notebook +267 -0
  12. passagemath_repl-10.5.1.data/scripts/sage-omega +25 -0
  13. passagemath_repl-10.5.1.data/scripts/sage-preparse +302 -0
  14. passagemath_repl-10.5.1.data/scripts/sage-run +27 -0
  15. passagemath_repl-10.5.1.data/scripts/sage-run-cython +10 -0
  16. passagemath_repl-10.5.1.data/scripts/sage-runtests +9 -0
  17. passagemath_repl-10.5.1.data/scripts/sage-startuptime.py +163 -0
  18. passagemath_repl-10.5.1.data/scripts/sage-valgrind +34 -0
  19. passagemath_repl-10.5.1.dist-info/METADATA +77 -0
  20. passagemath_repl-10.5.1.dist-info/RECORD +162 -0
  21. passagemath_repl-10.5.1.dist-info/WHEEL +5 -0
  22. passagemath_repl-10.5.1.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 +134 -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 +249 -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/all.py +0 -0
  143. sage/tests/all__sagemath_repl.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 +1925 -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 +796 -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
sage/misc/sagedoc.py ADDED
@@ -0,0 +1,1742 @@
1
+ # sage_setup: distribution = sagemath-repl
2
+ r"""
3
+ Format Sage documentation for viewing with IPython and the notebook
4
+
5
+ AUTHORS:
6
+
7
+ - William Stein (2005): initial version.
8
+ - Nick Alexander (2007): nodetex functions
9
+ - Nick Alexander (2008): search_src, search_def improvements
10
+ - Martin Albrecht (2008-03-21): parse LaTeX description environments in sagedoc
11
+ - John Palmieri (2009-04-11): fix for #5754 plus doctests
12
+ - Dan Drake (2009-05-21): refactor search_* functions, use system 'find' instead of sage -grep
13
+ - John Palmieri (2009-06-28): don't use 'find' -- use Python (os.walk, re.search) instead.
14
+ - Simon King (2011-09): Use os.linesep, avoid destruction of embedding information,
15
+ enable nodetex in a docstring. Consequently use sage_getdoc.
16
+
17
+ TESTS:
18
+
19
+ Check that argspecs of extension function/methods appear correctly,
20
+ see :issue:`12849`::
21
+
22
+ sage: from sage.env import SAGE_DOC
23
+ sage: docfilename = os.path.join(SAGE_DOC, 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html')
24
+ sage: with open(docfilename) as fobj: # needs sagemath_doc_html
25
+ ....: for line in fobj:
26
+ ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line:
27
+ ....: print(line)
28
+ <span class="sig-name descname"><span class="pre">numerical_approx</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">prec</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">digits</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">algorithm</span></span><span class="o"><span class="pre">=</span></span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span>...
29
+
30
+ Check that sphinx is not imported at Sage start-up::
31
+
32
+ sage: os.system("sage -c \"if 'sphinx' in sys.modules: sys.exit(1)\"") # needs sage.all
33
+ 0
34
+ """
35
+ # ****************************************************************************
36
+ # Copyright (C) 2005 William Stein <wstein@gmail.com>
37
+ #
38
+ # Distributed under the terms of the GNU General Public License (GPL)
39
+ # as published by the Free Software Foundation; either version 2 of
40
+ # the License, or (at your option) any later version.
41
+ # https://www.gnu.org/licenses/
42
+ # ****************************************************************************
43
+ import os
44
+ import re
45
+ import shutil
46
+ import sys
47
+ import pydoc
48
+ from sage.misc.temporary_file import tmp_dir
49
+ from sage.misc.viewer import browser
50
+ from sage.misc import sageinspect
51
+ import sage.version
52
+ from sage.env import SAGE_DOC, SAGE_SRC
53
+
54
+ # The detex function does two kinds of substitutions: math, which
55
+ # should only be done on the command line -- in the notebook, these
56
+ # should instead by taken care of by MathJax -- and nonmath, which
57
+ # should be done always.
58
+
59
+ # Math substitutions: don't forget the leading backslash '\\'. These
60
+ # are done using regular expressions, so it works best to also make
61
+ # the strings raw: r'\\blah'.
62
+ math_substitutes = [
63
+ (r'\\to', '-->'),
64
+ (r'\\rightarrow', '-->'),
65
+ (r'\\leftarrow', '<--'),
66
+ (r'\\leftrightarrow', '<->'),
67
+ (r'\\longrightarrow', '--->'),
68
+ (r'\\longleftarrow', '<---'),
69
+ (r'\\longleftrightarrow', '<-->'),
70
+ (r'\\Rightarrow', '==>'),
71
+ (r'\\Leftarrow', '<=='),
72
+ (r'\\Leftrightarrow', '<=>'),
73
+ (r'\\Longrightarrow', '===>'),
74
+ (r'\\Longleftarrow', '<==='),
75
+ (r'\\Longleftrightarrow', '<==>'),
76
+ (r'\\colon', ':'),
77
+ (r'\\left', ''),
78
+ (r'\\right', ''),
79
+ (r'\\bigl', ''),
80
+ (r'\\bigr', ''),
81
+ (r'\\leq', '<='),
82
+ (r'\\geq', '>='),
83
+ (r'\\le', '<='),
84
+ (r'\\ge', '>='),
85
+ (r'\\cdots', '...'),
86
+ (r'\\ldots', '...'),
87
+ (r'\\dots', '...'),
88
+ (r'\\cdot', ' *'),
89
+ (r'\\ast', ' *'),
90
+ (r' \\times', ' x'),
91
+ (r'\\times', ' x'),
92
+ (r'\\backslash', '\\'),
93
+ (r'\\mapsto', ' |--> '),
94
+ (r'\\longmapsto', ' |---> '),
95
+ (r'\\lvert', '|'),
96
+ (r'\\rvert', '|'),
97
+ (r'\\mid', '|'),
98
+ (r' \\circ', ' o'),
99
+ (r'\\circ', ' o')
100
+ ]
101
+ nonmath_substitutes = [
102
+ ('\\_', '_'),
103
+ ('\\item', '* '),
104
+ ('<BLANKLINE>', ''),
105
+ ('\\bf', ''),
106
+ ('\\sage', 'Sage'),
107
+ ('\\SAGE', 'Sage'),
108
+ ('\\Sage', 'Sage'),
109
+ ('\\rm', ''),
110
+ ('backslash', '\\'),
111
+ ('begin{enumerate}', ''),
112
+ ('end{enumerate}', ''),
113
+ ('begin{description}', ''),
114
+ ('end{description}', ''),
115
+ ('begin{itemize}', ''),
116
+ ('end{itemize}', ''),
117
+ ('begin{verbatim}', ''),
118
+ ('end{verbatim}', ''),
119
+ ('note{', 'NOTE: '),
120
+ ]
121
+
122
+
123
+ def _rmcmd(s, cmd, left='', right=''):
124
+ """
125
+ Remove the LaTeX command ``cmd`` from the string ``s``. This
126
+ function is used by ``detex``.
127
+
128
+ INPUT:
129
+
130
+ - ``s`` -- string; string from which to remove the command
131
+
132
+ - ``cmd`` -- string; command to be removed. This should be a
133
+ command which takes a single argument, like 'emph' or 'url'; the
134
+ command is removed, but its argument is not.
135
+
136
+ - ``left``, ``right`` -- string (default: ``''``); add these
137
+ strings at the left and right ends of the command. See the
138
+ examples.
139
+
140
+ EXAMPLES::
141
+
142
+ sage: from sage.misc.sagedoc import _rmcmd
143
+ sage: _rmcmd('Check out \\url{http://www.sagemath.org}.', 'url')
144
+ 'Check out http://www.sagemath.org.'
145
+ sage: _rmcmd('Text in \\emph{italics} looks like this.', 'emph', '*', '*')
146
+ 'Text in *italics* looks like this.'
147
+ sage: _rmcmd('This is a \\very{silly} example.', 'very', right='!?')
148
+ 'This is a silly!? example.'
149
+ """
150
+ c = '\\%s{' % cmd
151
+ while True:
152
+ i = s.find(c)
153
+ if i == -1:
154
+ return s
155
+ nesting = 1
156
+ j = i + len(c) + 1
157
+ while j < len(s) and nesting > 0:
158
+ if s[j] == '{':
159
+ nesting += 1
160
+ elif s[j] == '}':
161
+ nesting -= 1
162
+ j += 1
163
+ j -= 1 # j is position of closing '}'
164
+ if j < len(s):
165
+ s = s[:i] + left + s[i + len(c):j] + right + s[j + 1:]
166
+ else:
167
+ return s
168
+
169
+ # I wanted to be cool and use regexp's, but they aren't really
170
+ # useful, since really this is a parsing problem, because of
171
+ # nesting of commands, etc. Since it doesn't have to be
172
+ # super super fast (it's a page of text scrolled to the user),
173
+ # the above works fine.
174
+
175
+ #
176
+ # import re
177
+ # def _rmcmd(s, cmd, left='', right=''):
178
+ # c = '\\%s{.*}'%cmd
179
+ # r = re.compile(c, re.DOTALL)
180
+ # while True:
181
+ # m = r.search(s)
182
+ # if m is None: break
183
+ # s = s[:m.start()] + left + s[m.start()+len(cmd)+1:m.end()-1] \
184
+ # + right + s[m.end():]
185
+ # return s
186
+
187
+
188
+ itempattern = re.compile(r"\\item\[?([^]]*)\]? *(.*)")
189
+ itemreplace = r"* \1 \2"
190
+
191
+
192
+ def detex(s, embedded=False):
193
+ r"""nodetex
194
+ This strips LaTeX commands from a string; it is used by the
195
+ ``format`` function to process docstrings for display from the
196
+ command line interface.
197
+
198
+ INPUT:
199
+
200
+ - ``s`` -- string
201
+ - ``embedded`` -- boolean (default: ``False``)
202
+
203
+ If ``embedded`` is ``False``, then do the replacements in both
204
+ ``math_substitutes`` and ``nonmath_substitutes``. If ``True``, then
205
+ only do ``nonmath_substitutes``.
206
+
207
+ OUTPUT: string
208
+
209
+ EXAMPLES::
210
+
211
+ sage: from sage.misc.sagedoc import detex
212
+ sage: detex(r'Some math: `n \geq k`. A website: \url{sagemath.org}.')
213
+ 'Some math: n >= k. A website: sagemath.org.\n'
214
+ sage: detex(r'More math: `x \mapsto y`. {\bf Bold face}.')
215
+ 'More math: x |--> y. { Bold face}.\n'
216
+ sage: detex(r'`a, b, c, \ldots, z`')
217
+ 'a, b, c, ..., z\n'
218
+ sage: detex(r'`a, b, c, \ldots, z`', embedded=True)
219
+ '`a, b, c, \\ldots, z`'
220
+ sage: detex(r'`\left(\lvert x\ast y \rvert\right]`')
221
+ '(| x * y |]\n'
222
+ sage: detex(r'`\left(\leq\le\leftarrow \rightarrow\unknownmacro\to`')
223
+ '(<=<=<-- -->\\unknownmacro-->\n'
224
+ """
225
+ s = _rmcmd(s, 'url')
226
+ s = _rmcmd(s, 'code')
227
+ s = _rmcmd(s, 'class')
228
+ s = _rmcmd(s, 'mbox')
229
+ s = _rmcmd(s, 'text')
230
+ s = _rmcmd(s, 'section')
231
+ s = _rmcmd(s, 'subsection')
232
+ s = _rmcmd(s, 'subsubsection')
233
+ s = _rmcmd(s, 'note', 'NOTE: ', '')
234
+ s = _rmcmd(s, 'emph', '*', '*')
235
+ s = _rmcmd(s, 'textbf', '*', '*')
236
+
237
+ s = re.sub(itempattern, itemreplace, s)
238
+
239
+ for a, b in nonmath_substitutes:
240
+ s = s.replace(a, b)
241
+ if not embedded: # not in the notebook
242
+ s = _rmcmd(s, 'mathop')
243
+ s = _rmcmd(s, 'mathrm')
244
+ try:
245
+ from .sphinxify import sphinxify
246
+ except ImportError:
247
+ s = s.replace('``', '"').replace('`', '') + '\n'
248
+ else:
249
+ s = sphinxify(s, format='text')
250
+ # Do math substitutions. The strings to be replaced should be
251
+ # TeX commands like "\\blah". Do a regular expression
252
+ # replacement to replace "\\blah" but not "\\blahxyz", etc.:
253
+ # test to make sure the next character is not a letter.
254
+ for a, b in math_substitutes:
255
+ s = re.sub(a + '([^a-zA-Z])', b + '\\1', s)
256
+ return s
257
+
258
+
259
+ def skip_TESTS_block(docstring):
260
+ r"""
261
+ Remove blocks labeled "TESTS:" from ``docstring``.
262
+
263
+ INPUT:
264
+
265
+ - ``docstring`` -- string
266
+
267
+ A "TESTS" block is a block starting "TESTS:" (or
268
+ the same with two colons), on a line on its own, and ending either
269
+ with a line indented less than "TESTS", or with a line with the
270
+ same level of indentation -- not more -- matching one of the
271
+ following:
272
+
273
+ - a Sphinx directive of the form ".. foo:", optionally followed by
274
+ other text.
275
+
276
+ - text of the form "UPPERCASE:", optionally followed by other
277
+ text.
278
+
279
+ - lines which look like a reST header: one line containing
280
+ anything, followed by a line consisting only of a string of
281
+ hyphens, equal signs, or other characters which are valid
282
+ markers for reST headers: ``- = ` : ' " ~ _ ^ * + # < >``.
283
+ However, lines only containing double colons `::` do not
284
+ end "TESTS" blocks.
285
+
286
+ Return the string obtained from ``docstring`` by removing these
287
+ blocks.
288
+
289
+ EXAMPLES::
290
+
291
+ sage: from sage.misc.sagedoc import skip_TESTS_block
292
+ sage: start = ' Docstring\n\n'
293
+ sage: test = ' TESTS: \n\n Here is a test::\n sage: 2+2 \n 5 \n\n'
294
+ sage: test2 = ' TESTS:: \n\n sage: 2+2 \n 6 \n\n'
295
+
296
+ Test lines starting with "REFERENCES:"::
297
+
298
+ sage: refs = ' REFERENCES: \n text text \n'
299
+ sage: skip_TESTS_block(start + test + refs).rstrip() == (start + refs).rstrip()
300
+ True
301
+ sage: skip_TESTS_block(start + test + test2 + refs).rstrip() == (start + refs).rstrip()
302
+ True
303
+ sage: skip_TESTS_block(start + test + refs + test2).rstrip() == (start + refs).rstrip()
304
+ True
305
+
306
+ Test Sphinx directives::
307
+
308
+ sage: directive = ' .. todo:: \n do some stuff \n'
309
+ sage: skip_TESTS_block(start + test + refs + test2 + directive).rstrip() == (start + refs + directive).rstrip()
310
+ True
311
+
312
+ Test unindented lines::
313
+
314
+ sage: unindented = 'NOT INDENTED\n'
315
+ sage: skip_TESTS_block(start + test + unindented).rstrip() == (start + unindented).rstrip()
316
+ True
317
+ sage: skip_TESTS_block(start + test + unindented + test2 + unindented).rstrip() == (start + unindented + unindented).rstrip()
318
+ True
319
+
320
+ Test headers::
321
+
322
+ sage: header = ' Header:\n ~~~~~~~~'
323
+ sage: skip_TESTS_block(start + test + header) == start + header
324
+ True
325
+
326
+ Not a header because the characters on the second line must all be
327
+ the same::
328
+
329
+ sage: fake_header = ' Header:\n -=-=-=-=-='
330
+ sage: skip_TESTS_block(start + test + fake_header).rstrip() == start.rstrip()
331
+ True
332
+
333
+ Not a header because it's indented compared to 'TEST' in the
334
+ string ``test``::
335
+
336
+ sage: another_fake = '\n blah\n ----'
337
+ sage: skip_TESTS_block(start + test + another_fake).rstrip() == start.rstrip()
338
+ True
339
+
340
+ Double colons ``::`` are also not considered as headers (:issue:`27896`)::
341
+
342
+ sage: colons = ' ::\n\n sage: 2+2\n 4\n\n'
343
+ sage: skip_TESTS_block(start + test2 + colons).rstrip() == start.rstrip()
344
+ True
345
+ """
346
+ # tests_block: match a line starting with whitespace, then
347
+ # "TEST" or "TESTS" followed by ":" or "::", then possibly
348
+ # more whitespace, then the end of the line.
349
+ tests_block = re.compile('([ ]*)TEST[S]?:[:]?[ ]*$')
350
+ # end_of_block: match a line starting with whitespace, then Sphinx
351
+ # directives of the form ".. foo:". This will match directive
352
+ # names "foo" containing letters of either case, hyphens,
353
+ # underscores.
354
+ # Also match uppercase text followed by a colon, like
355
+ # "REFERENCES:" or "ALGORITHM:".
356
+ end_of_block = re.compile(r'[ ]*(\.\.[ ]+[-_A-Za-z]+|[A-Z]+):')
357
+ # header: match a string of hyphens, or other characters which are
358
+ # valid markers for reST headers: - = ` : ' " ~ _ ^ * + # < >
359
+ # except for double colons ::
360
+ header = re.compile(r'^[ ]*(?:([-=`\'"~_^*+#><])\1+|:|:::+)[ ]*$')
361
+ s = ''
362
+ skip = False
363
+ previous = ''
364
+ # indentation: amount of indentation at the start of 'TESTS:'.
365
+ indentation = ''
366
+ for l in docstring.split('\n'):
367
+ if not skip:
368
+ m = tests_block.match(l)
369
+ if m:
370
+ skip = True
371
+ indentation = m.group(1)
372
+ else:
373
+ s += "\n"
374
+ s += l
375
+ else:
376
+ if l and not l.isspace() and not l.startswith(indentation):
377
+ # A non-blank line indented less than 'TESTS:'
378
+ skip = False
379
+ s += "\n"
380
+ s += l
381
+ elif end_of_block.match(l) and not tests_block.match(l):
382
+ # A line matching end_of_block and indented the same as 'TESTS:'
383
+ if l.startswith(indentation + " "):
384
+ continue
385
+ skip = False
386
+ s += "\n"
387
+ s += l
388
+ elif header.match(l):
389
+ # A line matching header.
390
+ if l.startswith(indentation + " "):
391
+ continue
392
+ skip = False
393
+ if previous:
394
+ s += "\n"
395
+ s += previous
396
+ s += "\n"
397
+ s += l
398
+ previous = l
399
+ return s[1:] # Remove empty line from the beginning.
400
+
401
+
402
+ def process_dollars(s):
403
+ r"""nodetex
404
+ Replace dollar signs with backticks.
405
+
406
+ More precisely, do a regular expression search. Replace a plain
407
+ dollar sign ($) by a backtick (`). Replace an escaped dollar sign
408
+ (\\$) by a dollar sign ($). Don't change a dollar sign preceded or
409
+ followed by a backtick (\`$ or \$`), because of strings like
410
+ "``$HOME``". Don't make any changes on lines starting with more
411
+ spaces than the first nonempty line in ``s``, because those are
412
+ indented and hence part of a block of code or examples.
413
+
414
+ This also doesn't replaces dollar signs enclosed in curly braces,
415
+ to avoid nested math environments.
416
+
417
+ EXAMPLES::
418
+
419
+ sage: from sage.misc.sagedoc import process_dollars
420
+ sage: process_dollars('hello')
421
+ 'hello'
422
+ sage: process_dollars('some math: $x=y$')
423
+ doctest:warning...
424
+ DeprecationWarning: using dollar signs to mark up math in Sage docstrings
425
+ is deprecated; use backticks instead
426
+ See https://github.com/sagemath/sage/issues/33973 for details.
427
+ 'some math: `x=y`'
428
+
429
+ Replace \\$ with $, and don't do anything when backticks are involved::
430
+
431
+ sage: process_dollars(r'a ``$REAL`` dollar sign: \$')
432
+ 'a ``$REAL`` dollar sign: $'
433
+
434
+ Don't make any changes on lines indented more than the first
435
+ nonempty line::
436
+
437
+ sage: s = '\n first line\n indented $x=y$'
438
+ sage: s == process_dollars(s)
439
+ True
440
+
441
+ Don't replace dollar signs enclosed in curly braces::
442
+
443
+ sage: process_dollars(r'f(n) = 0 \text{ if $n$ is prime}')
444
+ 'f(n) = 0 \\text{ if $n$ is prime}'
445
+
446
+ This is not perfect::
447
+
448
+ sage: process_dollars(r'$f(n) = 0 \text{ if $n$ is prime}$')
449
+ '`f(n) = 0 \\text{ if $n$ is prime}$'
450
+
451
+ The regular expression search doesn't find the last $.
452
+ Fortunately, there don't seem to be any instances of this kind of
453
+ expression in the Sage library, as of this writing.
454
+ """
455
+ if s.find("$") == -1:
456
+ return s
457
+ from sage.misc.superseded import deprecation
458
+ # find how much leading whitespace s has, for later comparison:
459
+ # ignore all $ on lines which start with more whitespace.
460
+ whitespace = re.match(r'\s*\S', s.lstrip('\n'))
461
+ whitespace = ' ' * (whitespace.end() - 1) # leading whitespace
462
+ # Indices will be a list of pairs of positions in s, to search between.
463
+ # If the following search has no matches, then indices will be (0, len(s)).
464
+ indices = [0]
465
+ # This searches for "$blah$" inside a pair of curly braces --
466
+ # don't change these, since they're probably coming from a nested
467
+ # math environment. So for each match, search to the left of its
468
+ # start and to the right of its end, but not in between.
469
+ for m in re.finditer(r"{[^{}$]*\$([^{}$]*)\$[^{}$]*}", s):
470
+ indices[-1] = (indices[-1], m.start())
471
+ indices.append(m.end())
472
+ indices[-1] = (indices[-1], len(s))
473
+ # regular expression for $ (not \$, `$, $`, and only on a line
474
+ # with no extra leading whitespace).
475
+ #
476
+ # in detail:
477
+ # re.compile("^" # beginning of line
478
+ # + "(%s%)?" % whitespace
479
+ # + r"""(\S # non whitespace
480
+ # .*?)? # non-greedy match any non-newline characters
481
+ # (?<!`|\\)\$(?!`) # $ with negative lookbehind and lookahead
482
+ # """, re.M | re.X)
483
+ #
484
+ # except that this doesn't work, so use the equivalent regular
485
+ # expression without the 're.X' option. Maybe 'whitespace' gets
486
+ # eaten up by re.X?
487
+ regexp = "^" + "(%s)?" % whitespace + r"(\S.*?)?(?<!`|\\)\$(?!`)"
488
+ dollar = re.compile(regexp, re.M)
489
+ # regular expression for \$
490
+ slashdollar = re.compile(r"\\\$")
491
+ for start, end in indices:
492
+ while dollar.search(s, start, end):
493
+ m = dollar.search(s, start, end)
494
+ s = s[:m.end() - 1] + "`" + s[m.end():]
495
+ deprecation(33973,
496
+ "using dollar signs to mark up math in Sage docstrings "
497
+ "is deprecated; use backticks instead")
498
+ while slashdollar.search(s, start, end):
499
+ m = slashdollar.search(s, start, end)
500
+ s = s[:m.start()] + "$" + s[m.end():]
501
+ return s
502
+
503
+
504
+ # When adding roles here, also add them to SAGE_ROOT/src/tox.ini [flake8]
505
+ # and document them in SAGE_ROOT/src/doc/en/developer/sage_manuals.rst
506
+ #
507
+ # Sage github issue shortcuts. For example, :issue:`7549`.
508
+ pythonversion = sys.version.split(' ')[0]
509
+ extlinks = {
510
+ 'python': (f'https://docs.python.org/release/{pythonversion}/%s', None),
511
+ 'issue': ('https://github.com/sagemath/sage/issues/%s', 'Issue #%s'),
512
+ 'sage_root': ('https://github.com/sagemath/sage/tree/develop/%s', 'SAGE_ROOT/%s'),
513
+ 'wikipedia': ('https://en.wikipedia.org/wiki/%s', 'Wikipedia article %s'),
514
+ 'arxiv': ('https://arxiv.org/abs/%s', 'arXiv %s'),
515
+ 'oeis': ('https://oeis.org/%s', 'OEIS sequence %s'),
516
+ 'doi': ('https://doi.org/%s', 'doi:%s'),
517
+ 'pari': ('https://pari.math.u-bordeaux.fr/dochtml/help/%s', 'pari:%s'),
518
+ 'mathscinet': ('https://www.ams.org/mathscinet-getitem?mr=%s', 'MathSciNet %s'),
519
+ 'common_lisp': ('https://www.lispworks.com/documentation/lw50/CLHS/Body/%s.htm', 'Common Lisp: %s'),
520
+ 'ecl': ('https://ecl.common-lisp.dev/static/manual/%s.html', 'ECL: %s'),
521
+ 'gap': ('https://docs.gap-system.org/doc/ref/%s_mj.html', 'GAP: %s'),
522
+ 'gap_package': ('https://docs.gap-system.org/pkg/%s', 'GAP package %s'),
523
+ 'giac_cascmd': ('https://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/cascmd_en/%s.html', 'Giac: %s'),
524
+ 'giac_us': ('https://www-fourier.ujf-grenoble.fr/~parisse/giac_us.html#%s', 'Giac API: %s'),
525
+ 'maxima': ('https://maxima.sourceforge.io/docs/manual/maxima_singlepage.html#%s', 'Maxima: %s'),
526
+ 'meson': ('https://mesonbuild.com/%s', 'Meson: %s'),
527
+ 'polymake': ('https://polymake.org/doku.php/documentation/latest/%s', 'polymake: %s'),
528
+ 'ppl': ('https://www.bugseng.com/products/ppl/documentation/user/ppl-user-1.2-html/%s.html', 'PPL: %s'),
529
+ 'qepcad': ('https://www.usna.edu/CS/qepcadweb/B/%s.html', 'QEPCAD: %s'),
530
+ 'scip': ('https://scipopt.org/doc/html/%s.php', 'SCIP: %s'),
531
+ 'singular': ('https://www.singular.uni-kl.de/Manual/4-3-2/%s.htm', 'Singular: %s'),
532
+ 'soplex': ('https://soplex.zib.de/doc/html/%s.php', 'SoPlex: %s'),
533
+ }
534
+
535
+
536
+ def process_extlinks(s, embedded=False):
537
+ r"""nodetex
538
+
539
+ In docstrings at the command line, process markup related to the
540
+ Sphinx extlinks extension. For example, replace ``:issue:`NUM```
541
+ with ``https://github.com/sagemath/sage/issues/NUM``, and similarly with
542
+ ``:python:TEXT`` and ``:wikipedia:TEXT``, looking up the url from
543
+ the dictionary ``extlinks`` in ``sage_docbuild.conf``.
544
+ If ``TEXT`` is of the form ``blah <LINK>``, then it uses ``LINK``
545
+ rather than ``TEXT`` to construct the url.
546
+
547
+ In the notebook, don't do anything: let sphinxify take care of it.
548
+
549
+ INPUT:
550
+
551
+ - ``s`` -- string, in practice a docstring
552
+ - ``embedded`` -- boolean (default: ``False``)
553
+
554
+ This function is called by :func:`format`, and if in the notebook,
555
+ it sets ``embedded`` to be ``True``, otherwise ``False``.
556
+
557
+ EXAMPLES::
558
+
559
+ sage: from sage.misc.sagedoc import process_extlinks
560
+ sage: process_extlinks('See :issue:`1234`, :wikipedia:`Wikipedia <Sage_(mathematics_software)>`, and :issue:`4321` ...')
561
+ 'See https://github.com/sagemath/sage/issues/1234, https://en.wikipedia.org/wiki/Sage_(mathematics_software), and https://github.com/sagemath/sage/issues/4321 ...'
562
+ sage: process_extlinks('See :issue:`1234` for more information.', embedded=True)
563
+ 'See :issue:`1234` for more information.'
564
+ sage: process_extlinks('see :python:`Implementing Descriptors <reference/datamodel.html#implementing-descriptors>` ...')
565
+ 'see https://docs.python.org/release/.../reference/datamodel.html#implementing-descriptors ...'
566
+ """
567
+ if embedded:
568
+ return s
569
+ for key in extlinks:
570
+ while True:
571
+ m = re.search(':%s:`([^`]*)`' % key, s)
572
+ if not m:
573
+ break
574
+ link = m.group(1)
575
+ m = re.search('.*<([^>]*)>', link)
576
+ if m:
577
+ link = m.group(1)
578
+ s = re.sub(':%s:`([^`]*)`' % key,
579
+ extlinks[key][0].replace('%s', link),
580
+ s, count=1)
581
+ return s
582
+
583
+
584
+ def process_mathtt(s):
585
+ r"""nodetex
586
+ Replace \\mathtt{BLAH} with BLAH in the command line.
587
+
588
+ INPUT:
589
+
590
+ - ``s`` -- string, in practice a docstring
591
+
592
+ This function is called by :func:`format`.
593
+
594
+ EXAMPLES::
595
+
596
+ sage: from sage.misc.sagedoc import process_mathtt
597
+ sage: process_mathtt(r'e^\mathtt{self}')
598
+ 'e^self'
599
+ """
600
+ while True:
601
+ start = s.find("\\mathtt{")
602
+ end = s.find("}", start)
603
+ if start == -1 or end == -1:
604
+ break
605
+ s = s[:start] + s[start + 8:end] + s[end + 1:]
606
+ return s
607
+
608
+
609
+ def process_optional_doctest_tags(s):
610
+ r"""
611
+ Remove ``# optional/needs`` doctest tags for present features from docstring ``s``.
612
+
613
+ EXAMPLES:
614
+
615
+ sage: from sage.misc.sagedoc import process_optional_doctest_tags
616
+ sage: process_optional_doctest_tags("sage: # needs sage.rings.finite_rings\nsage: K.<x> = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2") # needs sage.rings.finite_rings
617
+ "sage: K.<x> = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2"
618
+ """
619
+ import io
620
+ from sage.doctest.external import available_software
621
+ from sage.doctest.parsing import parse_optional_tags, update_optional_tags
622
+
623
+ start = 0
624
+ with io.StringIO() as output:
625
+ for m in re.finditer('( *sage: *.*#.*)\n', s):
626
+ output.write(s[start:m.start(0)])
627
+ line = m.group(1)
628
+ tags = [tag for tag in parse_optional_tags(line)
629
+ if tag not in available_software]
630
+ line = update_optional_tags(line, tags=tags)
631
+ if not re.fullmatch(' *sage: *', line):
632
+ print(line, file=output)
633
+ start = m.end(0)
634
+ output.write(s[start:])
635
+ return output.getvalue()
636
+
637
+
638
+ def format(s, embedded=False):
639
+ r"""noreplace
640
+ Format Sage documentation ``s`` for viewing with IPython.
641
+
642
+ This calls :func:`detex` on ``s`` to convert LaTeX commands to plain
643
+ text, unless the directive ``nodetex`` is given in the first line
644
+ of the string.
645
+
646
+ Also, if ``s`` contains a string of the form ``<<<obj>>>``, then
647
+ it replaces it with the docstring for ``obj``, unless the
648
+ directive ``noreplace`` is given in the first line. If an error
649
+ occurs under the attempt to find the docstring for ``obj``, then
650
+ the substring ``<<<obj>>>`` is preserved.
651
+
652
+ Directives must be separated by a comma.
653
+
654
+ INPUT:
655
+
656
+ - ``s`` -- string
657
+ - ``embedded`` -- boolean (default: ``False``)
658
+
659
+ OUTPUT: string
660
+
661
+ Set ``embedded`` equal to ``True`` if formatting for use in the
662
+ notebook; this just gets passed as an argument to :func:`detex`.
663
+
664
+ .. SEEALSO::
665
+
666
+ :func:`sage.misc.sageinspect.sage_getdoc` to get the formatted
667
+ documentation of a given object.
668
+
669
+ EXAMPLES::
670
+
671
+ sage: from sage.misc.sagedoc import format
672
+ sage: format('Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard')
673
+ 'Let A be an m by n (0,1)-matrix. We identify A with a chessboard\n'
674
+
675
+ If the first line of the string is 'nodetex', remove 'nodetex' but
676
+ don't modify any TeX commands::
677
+
678
+ sage: format("nodetex\n`x \\geq y`")
679
+ '`x \\geq y`'
680
+
681
+ Testing a string enclosed in triple angle brackets::
682
+
683
+ sage: format('<<<identity_matrix')
684
+ '<<<identity_matrix\n'
685
+ sage: format('identity_matrix>>>')
686
+ 'identity_matrix>>>\n'
687
+ sage: format('<<<identity_matrix>>>') # needs sage.modules
688
+ '...Definition: identity_matrix(...'
689
+ sage: format('<<<identity_matrix>>>')[:28] # needs sphinx
690
+ 'Definition: identity_matrix('
691
+
692
+ TESTS:
693
+
694
+ We check that the todo Sphinx extension is correctly activated::
695
+
696
+ sage: sage.misc.sagedoc.format(sage.combinat.ranker.on_fly.__doc__) # needs sphinx
697
+ " Return ... Todo: add tests as in combinat::rankers\n"
698
+
699
+ In the following use case, the ``nodetex`` directive would have been ignored prior
700
+ to :issue:`11815`::
701
+
702
+ sage: cython_code = ["def testfunc(x):",
703
+ ....: " '''",
704
+ ....: " nodetex",
705
+ ....: " This is a doc string with raw latex",
706
+ ....: "",
707
+ ....: " `x \\geq y`",
708
+ ....: " '''",
709
+ ....: " return -x"]
710
+ sage: cython('\n'.join(cython_code)) # needs sage.misc.cython
711
+ sage: from sage.misc.sageinspect import sage_getdoc
712
+ sage: print(sage_getdoc(testfunc)) # needs sage.misc.cython
713
+ <BLANKLINE>
714
+ This is a doc string with raw latex
715
+ <BLANKLINE>
716
+ `x \geq y`
717
+ <BLANKLINE>
718
+
719
+ We check that the ``noreplace`` directive works, even combined with
720
+ ``nodetex`` (see :issue:`11817`)::
721
+
722
+ sage: print(format('''nodetex, noreplace\n<<<identity_matrix>>>`\\not= 0`'''))
723
+ <<<identity_matrix>>>`\not= 0`
724
+
725
+ If replacement is impossible, then no error is raised::
726
+
727
+ sage: print(format('<<<bla\n<<<bla>>>\n<<<identity_matrix>>>')) # needs sage.modules
728
+ <<<bla <<<bla>>>
729
+ <BLANKLINE>
730
+ Definition: identity_matrix(ring, n=0, sparse=False)
731
+ <BLANKLINE>
732
+ This function is available as identity_matrix(...) and
733
+ matrix.identity(...).
734
+ <BLANKLINE>
735
+ Return the n x n identity matrix over the given ring.
736
+ ...
737
+
738
+ Check that backslashes are preserved in code blocks (:issue:`29140`)::
739
+
740
+ sage: format('::\n' # needs sphinx
741
+ ....: '\n'
742
+ ....: r' sage: print(r"\\\\.")' '\n'
743
+ ....: r' \\\\.')
744
+ ' sage: print(r"\\\\\\\\.")\n \\\\\\\\.\n'
745
+ sage: format(r'inline code ``\\\\.``')
746
+ 'inline code "\\\\\\\\."\n'
747
+ """
748
+ if not isinstance(s, str):
749
+ raise TypeError("s must be a string")
750
+
751
+ # Leading empty lines must be removed, since we search for directives
752
+ # in the first line.
753
+ s = s.lstrip(os.linesep)
754
+
755
+ # parse directives at beginning of docstring
756
+ # currently, only 'nodetex' and 'noreplace' are supported.
757
+ # 'no' + 'doctest' may be supported eventually (don't type that as
758
+ # one word, or the whole file will not be doctested).
759
+ first_newline = s.find(os.linesep)
760
+ if first_newline > -1:
761
+ first_line = s[:first_newline]
762
+ else:
763
+ first_line = s
764
+ # Moreover, we must strip blank space in order to get the directives
765
+ directives = [d.strip().lower() for d in first_line.split(',')]
766
+
767
+ if 'noreplace' in directives or 'nodetex' in directives:
768
+ s = s[first_newline + len(os.linesep):]
769
+
770
+ try:
771
+ import sage.all as toplevel
772
+ except ImportError:
773
+ try:
774
+ import sage.all__sagemath_polyhedra as toplevel
775
+ except ImportError:
776
+ try:
777
+ import sage.all__sagemath_categories as toplevel
778
+ except ImportError:
779
+ import sage.all__sagemath_objects as toplevel
780
+
781
+ docs = set()
782
+ if 'noreplace' not in directives:
783
+ i_0 = 0
784
+ while True:
785
+ i = s[i_0:].find("<<<")
786
+ if i == -1:
787
+ break
788
+ j = s[i_0 + i + 3:].find('>>>')
789
+ if j == -1:
790
+ break
791
+ obj = s[i_0 + i + 3:i_0 + i + 3 + j]
792
+ if obj in docs:
793
+ t = ''
794
+ elif obj.isidentifier():
795
+ try:
796
+ x = getattr(toplevel, obj)
797
+ except AttributeError:
798
+ # A pair <<<...>>> has been found, but the object not.
799
+ i_0 += i + 6 + j
800
+ continue
801
+ t0 = sage.misc.sageinspect.sage_getdef(x, obj)
802
+ t1 = sage.misc.sageinspect.sage_getdoc(x)
803
+ t = 'Definition: ' + t0 + '\n\n' + t1
804
+ docs.add(obj)
805
+ else:
806
+ # This is a simple heuristics to cover the case of
807
+ # a non-matching set of <<< and >>>
808
+ i_0 += i + 3
809
+ continue
810
+
811
+ s = s[:i_0 + i] + '\n' + t + s[i_0 + i + 6 + j:]
812
+ i_0 += i
813
+
814
+ if 'nodetex' not in directives:
815
+ s = process_dollars(s)
816
+ s = skip_TESTS_block(s)
817
+ if not embedded:
818
+ s = process_mathtt(s)
819
+ s = process_extlinks(s, embedded=embedded)
820
+ s = detex(s, embedded=embedded)
821
+
822
+ if not embedded:
823
+ s = process_optional_doctest_tags(s)
824
+
825
+ return s
826
+
827
+
828
+ def format_src(s):
829
+ """
830
+ Format Sage source code ``s`` for viewing with IPython.
831
+
832
+ If ``s`` contains a string of the form "<<<obj>>>", then it
833
+ replaces it with the source code for "obj".
834
+
835
+ INPUT:
836
+
837
+ - ``s`` -- string
838
+
839
+ OUTPUT: string
840
+
841
+ EXAMPLES::
842
+
843
+ sage: from sage.misc.sagedoc import format_src
844
+ sage: format_src('unladen swallow')
845
+ 'unladen swallow'
846
+ sage: format_src('<<<Sq>>>')[5:15] # needs sage.combinat sage.modules
847
+ 'Sq(*nums):'
848
+ """
849
+ if not isinstance(s, str):
850
+ raise TypeError("s must be a string")
851
+ docs = set()
852
+
853
+ try:
854
+ import sage.all
855
+ except ImportError:
856
+ pass
857
+
858
+ while True:
859
+ i = s.find("<<<")
860
+ if i == -1:
861
+ break
862
+ j = s[i + 3:].find('>>>')
863
+ if j == -1:
864
+ break
865
+ obj = s[i + 3:i + 3 + j]
866
+ if obj in docs:
867
+ t = ''
868
+ else:
869
+ x = eval('sage.all.%s' % obj, locals())
870
+ t = my_getsource(x)
871
+ docs.add(obj)
872
+ if t is None:
873
+ print(x)
874
+ t = ''
875
+ s = s[:i] + '\n' + t + s[i + 6 + j:]
876
+
877
+ return s
878
+
879
+
880
+ ###############################
881
+
882
+ def _search_src_or_doc(what, string, extra1='', extra2='', extra3='',
883
+ extra4='', extra5='', **kwargs):
884
+ r"""
885
+ Search the Sage library or documentation for lines containing
886
+ ``string`` and possibly some other terms. This function is used by
887
+ :func:`search_src`, :func:`search_doc`, and :func:`search_def`.
888
+
889
+ INPUT:
890
+
891
+ - ``what`` -- either ``'src'`` or ``'doc'``, according to whether you
892
+ are searching the documentation or source code
893
+ - the rest of the input is the same as :func:`search_src`,
894
+ :func:`search_doc`, and :func:`search_def`
895
+
896
+ OUTPUT:
897
+
898
+ If ``interact`` is ``False``, a string containing the results;
899
+ otherwise, there is no output and the results are presented
900
+ according to whether you are using the notebook or command-line
901
+ interface. In the command-line interface, each line of the results
902
+ has the form ``filename:num:line of code``, where ``num`` is the
903
+ line number in ``filename`` and ``line of code`` is the line that
904
+ matched your search terms.
905
+
906
+ EXAMPLES::
907
+
908
+ sage: from sage.misc.sagedoc import _search_src_or_doc
909
+ sage: print(_search_src_or_doc('src', r'matrix\(', # long time random
910
+ ....: 'incidence_structures', 'self',
911
+ ....: '^combinat', interact=False))
912
+ misc/sagedoc.py: sage: _search_src_or_doc('src', 'matrix(', 'incidence_structures', 'self', '^combinat', interact=False)
913
+ combinat/designs/incidence_structures.py: M1 = self.incidence_matrix()
914
+ combinat/designs/incidence_structures.py: A = self.incidence_matrix()
915
+ combinat/designs/incidence_structures.py: M = transpose(self.incidence_matrix())
916
+ combinat/designs/incidence_structures.py: def incidence_matrix(self):
917
+ combinat/designs/incidence_structures.py: A = self.incidence_matrix()
918
+ combinat/designs/incidence_structures.py: A = self.incidence_matrix()
919
+ combinat/designs/incidence_structures.py: #A = self.incidence_matrix()
920
+
921
+ TESTS:
922
+
923
+ The examples are nice, but marking them "random" means we're not
924
+ really testing if the function works, just that it completes. These
925
+ tests aren't perfect, but are reasonable.
926
+
927
+ ::
928
+
929
+ sage: from sage.misc.sagedoc import _search_src_or_doc
930
+ sage: len(_search_src_or_doc('src', r'matrix\(', 'incidence_structures', 'self', 'combinat', interact=False).splitlines()) > 1
931
+ True
932
+ sage: 'abvar/homology' in _search_src_or_doc('doc', 'homology', 'variety', interact=False) # long time (4s on sage.math, 2012), needs sagemath_doc_html
933
+ True
934
+ sage: 'divisors' in _search_src_or_doc('src', '^ *def prime', interact=False)
935
+ True
936
+
937
+ When passing ``interactive=True``, in a terminal session this will pass the
938
+ ``text/plain`` output to the configured pager, while in a notebook session
939
+ it will display the ``text/html`` output in the notebook's pager. However,
940
+ in a non-interactive session (as in the doctests) it should just print the
941
+ results to stdout::
942
+
943
+ sage: from sage.misc.sagedoc import _search_src_or_doc
944
+ sage: _search_src_or_doc('src', # long time
945
+ ....: r'def _search_src_or_doc\(',
946
+ ....: interact=True)
947
+ misc/sagedoc.py:...: def _search_src_or_doc(what, string, extra1='', extra2='', extra3='',
948
+ """
949
+
950
+ # process keyword arguments
951
+ interact = kwargs.get('interact', True)
952
+ path_re = kwargs.get('path_re', '')
953
+ module = kwargs.get('module', 'sage')
954
+ whole_word = kwargs.get('whole_word', False)
955
+ ignore_case = kwargs.get('ignore_case', True)
956
+ multiline = kwargs.get('multiline', False)
957
+
958
+ # done processing keywords
959
+ # define module, exts (file extension), title (title of search),
960
+ # base_path (top directory in which to search)
961
+ if what == 'src':
962
+ base_path = SAGE_SRC
963
+ if module.find('sage') == 0:
964
+ module = module[4:].lstrip(".") # remove 'sage' or 'sage.' from module
965
+ base_path = os.path.join(base_path, 'sage')
966
+ module = module.replace(".", os.sep)
967
+ exts = ['py', 'pyx', 'pxd']
968
+ title = 'Source Code'
969
+ else:
970
+ module = ''
971
+ exts = ['html']
972
+ title = 'Documentation'
973
+ base_path = os.path.join(SAGE_DOC, 'html')
974
+ if not os.path.exists(base_path):
975
+ print("""Warning: the Sage documentation is not available""")
976
+
977
+ strip = len(base_path)
978
+ results = []
979
+ # in regular expressions, '\bWORD\b' matches 'WORD' but not
980
+ # 'SWORD' or 'WORDS'. so if the user requests a whole_word
981
+ # search, append and prepend '\b' to each string.
982
+ regexp = string
983
+ extra_regexps = extras = [extra1, extra2, extra3, extra4, extra5]
984
+ if whole_word:
985
+ regexp = r'\b' + regexp + r'\b'
986
+ extra_regexps = [r'\b%s\b' % e for e in extra_regexps]
987
+ if ignore_case:
988
+ # 'flags' is a flag passed to re.search. use bit-wise or "|" to combine flags.
989
+ flags = re.IGNORECASE
990
+ else:
991
+ flags = 0
992
+
993
+ # done with preparation; ready to start search
994
+ for dirpath, dirs, files in os.walk(os.path.join(base_path, module)):
995
+ try:
996
+ dirs.remove('_static')
997
+ except ValueError:
998
+ pass
999
+ for f in files:
1000
+ if not f.startswith('.') and re.search(r"\.(" + "|".join(exts) + ")$", f):
1001
+ filename = os.path.join(dirpath, f)
1002
+ if re.search(path_re, filename):
1003
+ if multiline:
1004
+ with open(filename) as fobj:
1005
+ line = fobj.read()
1006
+ if re.search(regexp, line, flags):
1007
+ match_list = line
1008
+ else:
1009
+ match_list = None
1010
+ for extra in extra_regexps:
1011
+ if extra and match_list:
1012
+ if not re.search(extra, match_list):
1013
+ match_list = None
1014
+ if match_list:
1015
+ results.append(filename[strip:].lstrip("/") + '\n')
1016
+ else:
1017
+ with open(filename) as fobj:
1018
+ match_list = [(lineno, line)
1019
+ for lineno, line in enumerate(fobj)
1020
+ if re.search(regexp, line, flags)]
1021
+ for extra in extra_regexps:
1022
+ if extra:
1023
+ match_list = [s for s in match_list
1024
+ if re.search(extra, s[1], re.MULTILINE | flags)]
1025
+ for num, line in match_list:
1026
+ results.append('{}:{}:{}'.format(
1027
+ filename[strip:].lstrip('/'), num + 1, line))
1028
+
1029
+ text_results = ''.join(results).rstrip()
1030
+
1031
+ if not interact:
1032
+ return text_results
1033
+
1034
+ html_results = format_search_as_html(title, results, [string] + extras)
1035
+ # potentially used below
1036
+
1037
+ # Pass through the IPython pager in a mime bundle
1038
+ from IPython.core.page import page
1039
+ if not isinstance(text_results, str):
1040
+ text_results = text_results.decode('utf-8', 'replace')
1041
+
1042
+ page({
1043
+ 'text/plain': text_results,
1044
+ # 'text/html': html_results
1045
+ # don't return HTML results since they currently are not
1046
+ # correctly formatted for Jupyter use
1047
+ })
1048
+
1049
+
1050
+ def search_src(string, extra1='', extra2='', extra3='', extra4='',
1051
+ extra5='', **kwds):
1052
+ r"""
1053
+ Search Sage library source code for lines containing ``string``.
1054
+ The search is case-insensitive by default.
1055
+
1056
+ INPUT:
1057
+
1058
+ - ``string`` -- string to find in the Sage source code
1059
+
1060
+ - ``extra1``, ..., ``extra5`` -- additional strings to require when
1061
+ searching. Lines must match all of these, as well as ``string``
1062
+
1063
+ - ``whole_word`` -- (default: ``False``) if ``True``, search for
1064
+ ``string`` and ``extra1`` (etc.) as whole words only. This
1065
+ assumes that each of these arguments is a single word, not a
1066
+ regular expression, and it might have unexpected results if used
1067
+ with regular expressions.
1068
+
1069
+ - ``ignore_case`` -- boolean (default: ``True``); if ``False``, perform a
1070
+ case-sensitive search
1071
+
1072
+ - ``multiline`` -- (default: ``False``) if ``True``, search more
1073
+ than one line at a time. In this case, print any matching file
1074
+ names, but don't print line numbers.
1075
+
1076
+ - ``interact`` -- boolean (default: ``True``); if ``False``, return
1077
+ a string with all the matches. Otherwise, this function returns
1078
+ ``None``, and the results are displayed appropriately, according
1079
+ to whether you are using the notebook or the command-line
1080
+ interface. You should not ordinarily need to use this.
1081
+
1082
+ - ``path_re`` -- (default: ``''``) regular expression which
1083
+ the filename (including the path) must match
1084
+
1085
+ - ``module`` -- (default: ``'sage'``) the module in which to
1086
+ search. The default is 'sage', the entire Sage library. If
1087
+ ``module`` doesn't start with "sage", then the links in the
1088
+ notebook output may not function.
1089
+
1090
+ OUTPUT:
1091
+
1092
+ If ``interact`` is ``False``, then return a string with all of
1093
+ the matches, separated by newlines. On the other hand, if
1094
+ ``interact`` is ``True`` (the default), there is no output. Instead:
1095
+ at the command line, the search results are printed on the screen
1096
+ in the form ``filename:line_number:line of text``, showing the
1097
+ filename in which each match occurs, the line number where it
1098
+ occurs, and the actual matching line. (If ``multiline`` is ``True``,
1099
+ then only the filename is printed for each match.) The file paths
1100
+ in the output are relative to ``$SAGE_SRC``. In the
1101
+ notebook, each match produces a link to the actual file in which
1102
+ it occurs.
1103
+
1104
+ The ``string`` and ``extraN`` arguments are treated as regular
1105
+ expressions, as is ``path_re``, and errors will be raised if they
1106
+ are invalid. The matches will be case-insensitive unless
1107
+ ``ignore_case`` is ``False``.
1108
+
1109
+ .. NOTE::
1110
+
1111
+ The ``extraN`` parameters are present only because
1112
+ ``search_src(string, *extras, interact=False)``
1113
+ is not parsed correctly by Python 2.6; see http://bugs.python.org/issue1909.
1114
+
1115
+ EXAMPLES:
1116
+
1117
+ First note that without using ``interact=False``, this function
1118
+ produces no output, while with ``interact=False``, the output is a
1119
+ string. These examples almost all use this option, so that they
1120
+ have something to which to compare their output.
1121
+
1122
+ You can search for "matrix" by typing ``search_src("matrix")``.
1123
+ This particular search will produce many results::
1124
+
1125
+ sage: len(search_src("matrix", interact=False).splitlines()) # random # long time
1126
+ 9522
1127
+
1128
+ You can restrict to the Sage calculus code with
1129
+ ``search_src("matrix", module="sage.calculus")``, and this
1130
+ produces many fewer results::
1131
+
1132
+ sage: len(search_src("matrix", module="sage.calculus", interact=False).splitlines()) # random
1133
+ 26
1134
+
1135
+ Note that you can do tab completion on the ``module`` string.
1136
+ Another way to accomplish a similar search::
1137
+
1138
+ sage: len(search_src("matrix", path_re="calc", # needs sage.modules
1139
+ ....: interact=False).splitlines()) > 15
1140
+ True
1141
+
1142
+ The following produces an error because the string 'fetch(' is a
1143
+ malformed regular expression::
1144
+
1145
+ sage: print(search_src(" fetch(", "def", interact=False))
1146
+ Traceback (most recent call last):
1147
+ ...
1148
+ error: missing ), unterminated subpattern at position 6
1149
+
1150
+ To fix this, *escape* the parenthesis with a backslash::
1151
+
1152
+ sage: print(search_src(r" fetch\(", "def", interact=False)) # random # long time
1153
+ matrix/matrix0.pyx: cdef fetch(self, key):
1154
+ matrix/matrix0.pxd: cdef fetch(self, key)
1155
+
1156
+ sage: print(search_src(r" fetch\(", "def", "pyx", interact=False)) # random # long time
1157
+ matrix/matrix0.pyx: cdef fetch(self, key):
1158
+
1159
+ As noted above, the search is case-insensitive, but you can make it
1160
+ case-sensitive with the 'ignore_case' key word::
1161
+
1162
+ sage: s = search_src('Matrix', path_re='matrix', interact=False); s.find('x') > 0
1163
+ True
1164
+
1165
+ sage: s = search_src('MatRiX', path_re='matrix', interact=False); s.find('x') > 0
1166
+ True
1167
+
1168
+ sage: s = search_src('MatRiX', path_re='matrix',
1169
+ ....: interact=False, ignore_case=False); s.find('x') > 0
1170
+ False
1171
+
1172
+ Searches are by default restricted to single lines, but this can
1173
+ be changed by setting ``multiline`` to be True. In the following,
1174
+ since ``search_src(string, interact=False)`` returns a string with
1175
+ one line for each match, counting the length of
1176
+ ``search_src(string, interact=False).splitlines()`` gives the
1177
+ number of matches. ::
1178
+
1179
+ sage: len(search_src('log', 'derivative', interact=False).splitlines()) < 40
1180
+ True
1181
+ sage: len(search_src('log', 'derivative', # needs sage.all
1182
+ ....: interact=False, multiline=True).splitlines()) > 70
1183
+ True
1184
+
1185
+ A little recursive narcissism: let's do a doctest that searches for
1186
+ this function's doctests. Note that you can't put "sage:" in the
1187
+ doctest string because it will get replaced by the Python ">>>"
1188
+ prompt.
1189
+
1190
+ ::
1191
+
1192
+ sage: print(search_src(r'^ *sage[:] .*search_src\(', interact=False)) # long time
1193
+ misc/sagedoc.py:... len(search_src("matrix", interact=False).splitlines())...
1194
+ misc/sagedoc.py:... len(search_src("matrix", module="sage.calculus", interact=False).splitlines())...
1195
+ misc/sagedoc.py:... len(search_src("matrix", path_re="calc"...
1196
+ misc/sagedoc.py:... print(search_src(" fetch(", "def", interact=False))...
1197
+ misc/sagedoc.py:... print(search_src(r" fetch\(", "def", interact=False))...
1198
+ misc/sagedoc.py:... print(search_src(r" fetch\(", "def", "pyx", interact=False))...
1199
+ misc/sagedoc.py:... s = search_src('Matrix', path_re='matrix', interact=False); s.find('x') > 0...
1200
+ misc/sagedoc.py:... s = search_src('MatRiX', path_re='matrix', interact=False); s.find('x') > 0...
1201
+ misc/sagedoc.py:... s = search_src('MatRiX', path_re='matrix',...
1202
+ misc/sagedoc.py:... len(search_src('log', 'derivative', interact=False).splitlines()) < 40...
1203
+ misc/sagedoc.py:... len(search_src('log', 'derivative'...
1204
+ misc/sagedoc.py:... print(search_src(r'^ *sage[:] .*search_src\(', interact=False))...
1205
+ misc/sagedoc.py:... len(search_src("matrix", interact=False).splitlines()) > 9000...
1206
+ misc/sagedoc.py:... print(search_src('matrix', 'column', 'row', 'sub',...
1207
+ misc/sagedoc.py:... sage: results = search_src('format_search_as_html',...
1208
+
1209
+ TESTS:
1210
+
1211
+ As of this writing, there are about 9500 lines in the Sage library that
1212
+ contain "matrix"; it seems safe to assume we will continue to have
1213
+ over 9000 such lines::
1214
+
1215
+ sage: len(search_src("matrix", interact=False).splitlines()) > 9000 # long time
1216
+ True
1217
+
1218
+ Check that you can pass 5 parameters::
1219
+
1220
+ sage: print(search_src('matrix', 'column', 'row', 'sub', 'start', 'index', interact=False)) # random # long time
1221
+ matrix/matrix0.pyx:598: Get The 2 x 2 submatrix of M, starting at row index and column
1222
+ matrix/matrix0.pyx:607: Get the 2 x 3 submatrix of M starting at row index and column index
1223
+ matrix/matrix0.pyx:924: Set the 2 x 2 submatrix of M, starting at row index and column
1224
+ matrix/matrix0.pyx:933: Set the 2 x 3 submatrix of M starting at row index and column
1225
+ """
1226
+ return _search_src_or_doc('src', string, extra1=extra1, extra2=extra2,
1227
+ extra3=extra3, extra4=extra4, extra5=extra5,
1228
+ **kwds)
1229
+
1230
+
1231
+ def search_doc(string, extra1='', extra2='', extra3='', extra4='',
1232
+ extra5='', **kwds):
1233
+ r"""
1234
+ Search Sage HTML documentation for lines containing ``string``. The
1235
+ search is case-insensitive by default.
1236
+
1237
+ The file paths in the output are relative to ``$SAGE_DOC``.
1238
+
1239
+ INPUT: same as for :func:`search_src`.
1240
+
1241
+ OUTPUT: same as for :func:`search_src`
1242
+
1243
+ EXAMPLES:
1244
+
1245
+ See the documentation for :func:`search_src` for more examples. ::
1246
+
1247
+ sage: search_doc('creates a polynomial', path_re='tutorial', interact=False) # random
1248
+ html/en/tutorial/tour_polynomial.html:<p>This creates a polynomial ring and tells Sage to use (the string)
1249
+
1250
+ If you search the documentation for 'tree', then you will get too
1251
+ many results, because many lines in the documentation contain the
1252
+ word 'toctree'. If you use the ``whole_word`` option, though, you
1253
+ can search for 'tree' without returning all of the instances of
1254
+ 'toctree'. In the following, since ``search_doc('tree',
1255
+ interact=False)`` returns a string with one line for each match,
1256
+ counting the length of ``search_doc('tree',
1257
+ interact=False).splitlines()`` gives the number of matches. ::
1258
+
1259
+ sage: # long time, needs sagemath_doc_html
1260
+ sage: N = len(search_doc('tree', interact=False).splitlines())
1261
+ sage: L = search_doc('tree', whole_word=True, interact=False).splitlines()
1262
+ sage: len(L) < N
1263
+ True
1264
+ sage: import re
1265
+ sage: tree_re = re.compile(r'(^|\W)tree(\W|$)', re.I)
1266
+ sage: all(tree_re.search(l) for l in L)
1267
+ True
1268
+ """
1269
+ return _search_src_or_doc('doc', string, extra1=extra1, extra2=extra2,
1270
+ extra3=extra3, extra4=extra4, extra5=extra5,
1271
+ **kwds)
1272
+
1273
+
1274
+ def search_def(name, extra1='', extra2='', extra3='', extra4='',
1275
+ extra5='', **kwds):
1276
+ r"""
1277
+ Search Sage library source code for function definitions containing
1278
+ ``name``. The search is case-insensitive by default.
1279
+
1280
+ INPUT: same as for :func:`search_src`.
1281
+
1282
+ OUTPUT: same as for :func:`search_src`
1283
+
1284
+ .. NOTE::
1285
+
1286
+ The regular expression used by this function only finds function
1287
+ definitions that are preceded by spaces, so if you use tabs on a
1288
+ "def" line, this function will not find it. As tabs are not
1289
+ allowed in Sage library code, this should not be a problem.
1290
+
1291
+ EXAMPLES:
1292
+
1293
+ See the documentation for :func:`search_src` for more examples. ::
1294
+
1295
+ sage: print(search_def("fetch", interact=False)) # random # long time
1296
+ matrix/matrix0.pyx: cdef fetch(self, key):
1297
+ matrix/matrix0.pxd: cdef fetch(self, key)
1298
+
1299
+ sage: print(search_def("fetch", path_re="pyx", interact=False)) # random # long time
1300
+ matrix/matrix0.pyx: cdef fetch(self, key):
1301
+ """
1302
+ # since we convert name to a regular expression, we need to do the
1303
+ # 'whole_word' conversion here, rather than pass it on to
1304
+ # _search_src_or_doc.
1305
+ if 'whole_word' in kwds and kwds['whole_word']:
1306
+ name = r'\b' + name + r'\b'
1307
+ if extra1:
1308
+ extra1 = r'\b' + extra1 + r'\b'
1309
+ if extra2:
1310
+ extra2 = r'\b' + extra2 + r'\b'
1311
+ if extra3:
1312
+ extra3 = r'\b' + extra3 + r'\b'
1313
+ if extra4:
1314
+ extra4 = r'\b' + extra4 + r'\b'
1315
+ if extra5:
1316
+ extra5 = r'\b' + extra5 + r'\b'
1317
+ kwds['whole_word'] = False
1318
+
1319
+ return _search_src_or_doc('src', '^ *[c]?def.*%s' % name, extra1=extra1,
1320
+ extra2=extra2, extra3=extra3, extra4=extra4,
1321
+ extra5=extra5, **kwds)
1322
+
1323
+
1324
+ def format_search_as_html(what, results, search):
1325
+ r"""
1326
+ Format the output from ``search_src``, ``search_def``, or
1327
+ ``search_doc`` as html, for use in the notebook.
1328
+
1329
+ INPUT:
1330
+
1331
+ - ``what`` -- string; what was searched (source code or
1332
+ documentation)
1333
+ - ``results`` -- string or list; the results of the search as a string or list of
1334
+ search results
1335
+ - ``search`` -- string or list; what was being searched for, either as a
1336
+ string which is taken verbatim, or a list of multiple search terms if
1337
+ there were more than one
1338
+
1339
+ This function parses ``results``: each line should have either the form
1340
+ ``FILENAME`` or ``FILENAME: string`` where FILENAME is the file in which
1341
+ the string that matched the search was found. If FILENAME ends in '.html',
1342
+ then this is part of the documentation; otherwise, it is in the source
1343
+ code. In either case, an appropriate link is created.
1344
+
1345
+ EXAMPLES::
1346
+
1347
+ sage: from sage.misc.sagedoc import format_search_as_html
1348
+ sage: format_search_as_html('Source', 'algebras/steenrod_algebra_element.py: an antihomomorphism: if we call the antipode `c`, then', 'antipode antihomomorphism')
1349
+ '<html><font color="black"><h2>Search Source: "antipode antihomomorphism"</h2></font><font color="darkpurple"><ol><li><a href="/src/algebras/steenrod_algebra_element.py" target="_blank"><tt>algebras/steenrod_algebra_element.py</tt></a>\n</ol></font></html>'
1350
+ sage: format_search_as_html('Other', 'html/en/reference/sage/algebras/steenrod_algebra_element.html:an antihomomorphism: if we call the antipode <span class="math">c</span>, then', 'antipode antihomomorphism')
1351
+ '<html><font color="black"><h2>Search Other: "antipode antihomomorphism"</h2></font><font color="darkpurple"><ol><li><a href="/doc/live/reference/sage/algebras/steenrod_algebra_element.html" target="_blank"><tt>reference/sage/algebras/steenrod_algebra_element.html</tt></a>\n</ol></font></html>'
1352
+
1353
+ TESTS:
1354
+
1355
+ Test that results from a ``search_src`` with ``multiline=True`` works
1356
+ reasonably::
1357
+
1358
+ sage: results = search_src('format_search_as_html', # long time
1359
+ ....: multiline=True, interact=False)
1360
+ sage: format_search_as_html('Source', results, # long time
1361
+ ....: 'format_search_as_html')
1362
+ '<html><font color="black"><h2>Search Source: "format_search_as_html"</h2></font><font color="darkpurple"><ol><li><a href="/src/misc/sagedoc.py" target="_blank"><tt>misc/sagedoc.py</tt></a>\n</ol></font></html>'
1363
+ """
1364
+
1365
+ if not isinstance(search, list):
1366
+ search = [search]
1367
+
1368
+ s = [
1369
+ '<html>',
1370
+ '<font color="black">',
1371
+ '<h2>Search {}: {}</h2>'.format(
1372
+ what, ', '.join('"{}"'.format(s) for s in search if s.strip())),
1373
+ '</font>',
1374
+ '<font color="darkpurple">',
1375
+ '<ol>'
1376
+ ]
1377
+
1378
+ append = s.append
1379
+
1380
+ if not isinstance(results, list):
1381
+ results = results.splitlines()
1382
+
1383
+ files = set()
1384
+ for L in results:
1385
+ filename = L.strip().split(':', 1)[0]
1386
+ if filename:
1387
+ files.add(filename)
1388
+ files = sorted(files)
1389
+ for F in files:
1390
+ if F.endswith('.html'):
1391
+ F = F.split('/', 2)[2]
1392
+ url = '/doc/live/' + F
1393
+ else:
1394
+ # source code
1395
+ url = '/src/' + F
1396
+ append('<li><a href="%s" target="_blank"><tt>%s</tt></a>\n' % (url, F))
1397
+ append('</ol>')
1398
+ append('</font>')
1399
+ append('</html>')
1400
+ return ''.join(s)
1401
+
1402
+
1403
+ #######################################
1404
+ # Add detex'ing of documentation
1405
+ #######################################
1406
+
1407
+
1408
+ def my_getsource(obj, oname=''):
1409
+ """
1410
+ Retrieve the source code for ``obj``.
1411
+
1412
+ INPUT:
1413
+
1414
+ - ``obj`` -- a Sage object, function, etc.
1415
+
1416
+ - ``oname`` -- string (optional); a name under which the object is
1417
+ known. Currently ignored by Sage
1418
+
1419
+ OUTPUT:
1420
+
1421
+ Its documentation (string)
1422
+
1423
+ EXAMPLES::
1424
+
1425
+ sage: from sage.misc.sagedoc import my_getsource
1426
+ sage: s = my_getsource(identity_matrix) # needs sage.modules
1427
+ sage: s[15:34] # needs sage.modules
1428
+ 'def identity_matrix'
1429
+ """
1430
+ try:
1431
+ s = sageinspect.sage_getsource(obj)
1432
+ return format_src(s)
1433
+ except Exception as msg:
1434
+ print('Error getting source:', msg)
1435
+ return None
1436
+
1437
+
1438
+ class _sage_doc:
1439
+ """
1440
+ Open Sage documentation in a web browser, from either the
1441
+ command-line or the notebook.
1442
+
1443
+ - Type "browse_sage_doc.DOCUMENT()" to open the named document --
1444
+ for example, "browse_sage_doc.tutorial()" opens the tutorial.
1445
+ Available documents are
1446
+
1447
+ - tutorial: the Sage tutorial
1448
+ - reference: the Sage reference manual
1449
+ - constructions: "how do I construct ... in Sage?"
1450
+ - developer: the Sage developer's guide.
1451
+
1452
+ - Type "browse_sage_doc(OBJECT, output=FORMAT, view=BOOL)" to view
1453
+ the documentation for OBJECT, as in
1454
+ "browse_sage_doc(identity_matrix, 'html'). ``output`` can be
1455
+ either 'html' or 'rst': the form of the output. ``view`` is
1456
+ only relevant if ``output`` is ``html``; in this case, if
1457
+ ``view`` is ``True`` (its default value), then open up the
1458
+ documentation in a web browser. Otherwise, just output the
1459
+ documentation as a string.
1460
+
1461
+ EXAMPLES::
1462
+
1463
+ sage: browse_sage_doc._open("reference", testing=True)[0] # needs sagemath_doc_html
1464
+ 'http://localhost:8000/doc/live/reference/index.html'
1465
+ sage: browse_sage_doc(identity_matrix, 'rst')[-107:-47] # needs sage.modules
1466
+ 'Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring'
1467
+ """
1468
+ def __init__(self):
1469
+ """
1470
+ EXAMPLES::
1471
+
1472
+ sage: browse_sage_doc._base_url
1473
+ 'http://localhost:8000/doc/live/'
1474
+ """
1475
+ self._base_url = "http://localhost:8000/doc/live/"
1476
+ self._base_path = os.path.join(SAGE_DOC, "html", "en")
1477
+
1478
+ def __call__(self, obj, output='html', view=True):
1479
+ r"""
1480
+ Return the documentation for ``obj``.
1481
+
1482
+ INPUT:
1483
+
1484
+ - ``obj`` -- a Sage object
1485
+ - ``output`` -- 'html', 'rst', or 'text': return documentation in this form
1486
+ - ``view`` -- only has an effect if output is 'html': in this
1487
+ case, if ``view`` is ``True``, display the documentation in
1488
+ a web browser. Otherwise, return the documentation as a
1489
+ string.
1490
+
1491
+ EXAMPLES::
1492
+
1493
+ sage: browse_sage_doc(identity_matrix, 'rst') # needs sage.modules
1494
+ "...**File:**...**Type:**...**Definition:** identity_matrix..."
1495
+ sage: identity_matrix.__doc__ in browse_sage_doc(identity_matrix, 'rst') # needs sage.modules
1496
+ True
1497
+ sage: browse_sage_doc(identity_matrix, 'html', False) # needs sagemath_doc_html sphinx
1498
+ '...div...File:...Type:...Definition:...identity_matrix...'
1499
+
1500
+ In the 'text' version, double colons have been replaced with
1501
+ single ones (among other things)::
1502
+
1503
+ sage: '::' in browse_sage_doc(identity_matrix, 'rst') # needs sage.modules
1504
+ True
1505
+ sage: '::' in browse_sage_doc(identity_matrix, 'text') # needs sphinx
1506
+ False
1507
+ """
1508
+ if output != 'html' and view:
1509
+ view = False
1510
+
1511
+ s = ''
1512
+ newline = "\n\n" # blank line to start new paragraph
1513
+
1514
+ try:
1515
+ filename = sageinspect.sage_getfile(obj)
1516
+ s += '**File:** %s' % filename
1517
+ s += newline
1518
+ except TypeError:
1519
+ pass
1520
+
1521
+ obj_name = ''
1522
+ locs = sys._getframe(1).f_locals
1523
+ for var in locs:
1524
+ if id(locs[var]) == id(obj):
1525
+ obj_name = var
1526
+
1527
+ s += '**Type:** %s' % type(obj)
1528
+ s += newline
1529
+ s += '**Definition:** %s' % sageinspect.sage_getdef(obj, obj_name)
1530
+ s += newline
1531
+ s += '**Docstring:**'
1532
+ s += newline
1533
+ s += sageinspect.sage_getdoc(obj, obj_name, embedded=True)
1534
+
1535
+ # now s should be the reST version of the docstring
1536
+ if output == 'html':
1537
+ try:
1538
+ from .sphinxify import sphinxify
1539
+ except ImportError:
1540
+ from html import escape
1541
+ html = escape(s)
1542
+ else:
1543
+ html = sphinxify(s)
1544
+ if view:
1545
+ path = os.path.join(tmp_dir(), "temp.html")
1546
+ filed = open(path, 'w')
1547
+
1548
+ static_path = os.path.join(SAGE_DOC, "html", "en", "_static")
1549
+ if os.path.exists(static_path):
1550
+ title = obj_name + ' - Sage ' + sage.version.version + ' Documentation'
1551
+ template = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
1552
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1553
+
1554
+ <html xmlns="http://www.w3.org/1999/xhtml">
1555
+ <head>
1556
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1557
+ <title>%(title)s</title>
1558
+ <link rel="stylesheet" href="%(static_path)s/default.css" type="text/css" />
1559
+ <link rel="stylesheet" href="%(static_path)s/pygments.css" type="text/css" />
1560
+ <style type="text/css">
1561
+ <!--
1562
+ div.body {
1563
+ margin: 1.0em;
1564
+ padding: 1.0em;
1565
+ }
1566
+ div.bodywrapper {
1567
+ margin: 0;
1568
+ }
1569
+ -->
1570
+ </style>
1571
+ <script type="text/javascript">
1572
+ var DOCUMENTATION_OPTIONS = {
1573
+ URL_ROOT: '',
1574
+ VERSION: '%(version)s',
1575
+ COLLAPSE_MODINDEX: false,
1576
+ FILE_SUFFIX: '.html',
1577
+ HAS_SOURCE: false
1578
+ };
1579
+ </script>
1580
+ <script type="text/javascript" src="%(static_path)s/jquery.js"></script>
1581
+ <script type="text/javascript" src="%(static_path)s/doctools.js"></script>
1582
+ <link rel="shortcut icon" href="%(static_path)s/favicon.ico" />
1583
+ <link rel="icon" href="%(static_path)s/sageicon.png" type="image/x-icon" />
1584
+ </head>
1585
+ <body>
1586
+ <div class="document">
1587
+ <div class="documentwrapper">
1588
+ <div class="bodywrapper">
1589
+ <div class="body">
1590
+ %(html)s
1591
+ </div>
1592
+ </div>
1593
+ </div>
1594
+ </div>
1595
+ </body>
1596
+ </html>"""
1597
+ html = template % {'html': html,
1598
+ 'static_path': static_path,
1599
+ 'title': title,
1600
+ 'version': sage.version.version}
1601
+
1602
+ filed.write(html)
1603
+ filed.close()
1604
+ os.system(browser() + " " + path)
1605
+ else:
1606
+ return html
1607
+ elif output == 'rst':
1608
+ return s
1609
+ elif output == 'text':
1610
+ try:
1611
+ from .sphinxify import sphinxify
1612
+ except ImportError:
1613
+ return s
1614
+ else:
1615
+ return sphinxify(s, format='text')
1616
+ else:
1617
+ raise ValueError("output type {} not recognized".format(output))
1618
+
1619
+ def _open(self, name, testing=False):
1620
+ """
1621
+ Open the document ``name`` in a web browser. This constructs
1622
+ the appropriate URL and/or path name and passes it to the web
1623
+ browser.
1624
+
1625
+ INPUT:
1626
+
1627
+ - ``name`` -- string; name of the documentation
1628
+
1629
+ - ``testing`` -- boolean (default: ``False``); if ``True``,
1630
+ then just return the URL and path-name for this document
1631
+ (don't open the web browser)
1632
+
1633
+ EXAMPLES::
1634
+
1635
+ sage: browse_sage_doc._open("reference", testing=True)[0] # needs sagemath_doc_html
1636
+ 'http://localhost:8000/doc/live/reference/index.html'
1637
+ sage: browse_sage_doc._open("tutorial", testing=True)[1] # needs sagemath_doc_html
1638
+ '.../html/en/tutorial/index.html'
1639
+ """
1640
+ url = self._base_url + os.path.join(name, "index.html")
1641
+ path = os.path.join(self._base_path, name, "index.html")
1642
+ if not os.path.exists(path):
1643
+ raise OSError("""The document '{0}' does not exist. Please build it
1644
+ with 'sage -docbuild {0} html' and try again.""".format(name))
1645
+
1646
+ if testing:
1647
+ return (url, path)
1648
+
1649
+ os.system(browser() + " " + path)
1650
+
1651
+ def tutorial(self):
1652
+ """
1653
+ The Sage tutorial. To get started with Sage, start here.
1654
+
1655
+ EXAMPLES::
1656
+
1657
+ sage: tutorial() # indirect doctest, not tested
1658
+ """
1659
+ self._open("tutorial")
1660
+
1661
+ def reference(self):
1662
+ """
1663
+ The Sage reference manual.
1664
+
1665
+ EXAMPLES::
1666
+
1667
+ sage: reference() # indirect doctest, not tested
1668
+ sage: manual() # indirect doctest, not tested
1669
+ """
1670
+ self._open("reference")
1671
+
1672
+ manual = reference
1673
+
1674
+ def developer(self):
1675
+ """
1676
+ The Sage developer's guide. Learn to develop programs for Sage.
1677
+
1678
+ EXAMPLES::
1679
+
1680
+ sage: developer() # indirect doctest, not tested
1681
+ """
1682
+ self._open("developer")
1683
+
1684
+ def constructions(self):
1685
+ """
1686
+ Sage constructions. Attempts to answer the question "How do I
1687
+ construct ... in Sage?"
1688
+
1689
+ EXAMPLES::
1690
+
1691
+ sage: constructions() # indirect doctest, not tested
1692
+ """
1693
+ self._open("constructions")
1694
+
1695
+
1696
+ browse_sage_doc = _sage_doc()
1697
+ tutorial = browse_sage_doc.tutorial
1698
+ reference = browse_sage_doc.reference
1699
+ manual = browse_sage_doc.reference
1700
+ developer = browse_sage_doc.developer
1701
+ constructions = browse_sage_doc.constructions
1702
+
1703
+ python_help = pydoc.help
1704
+
1705
+
1706
+ def help(module=None):
1707
+ """
1708
+ If there is an argument ``module``, print the Python help message
1709
+ for ``module``. With no argument, print a help message about
1710
+ getting help in Sage.
1711
+
1712
+ EXAMPLES::
1713
+
1714
+ sage: help()
1715
+ Welcome to Sage ...
1716
+ """
1717
+ if module is not None:
1718
+ python_help(module)
1719
+ else:
1720
+ print("""Welcome to Sage {}!
1721
+
1722
+ To view the Sage tutorial in your web browser, type "tutorial()", and
1723
+ to view the (very detailed) Sage reference manual, type "manual()".
1724
+ For help on any Sage function, for example "matrix_plot", type
1725
+ "matrix_plot?" to see a help message, type "help(matrix_plot)" to see
1726
+ a very similar message, type "browse_sage_doc(matrix_plot)" to view a
1727
+ help message in a web browser, and type "matrix_plot??" to look at the
1728
+ function's source code.
1729
+
1730
+ (When you type something like "matrix_plot?", "help(matrix_plot)", or
1731
+ "matrix_plot??", Sage may start a paging program to display the
1732
+ requested message. Type a space to scroll to the next page, type "h"
1733
+ to get help on the paging program, and type "q" to quit it and return
1734
+ to the "sage:" prompt.)
1735
+
1736
+ For license information for Sage and its components, read the file
1737
+ "COPYING.txt" in the top-level directory of the Sage installation,
1738
+ or type "license()".
1739
+
1740
+ To enter Python's interactive online help utility, type "python_help()".
1741
+ To get help on a Python function, module or package, type "help(MODULE)" or
1742
+ "python_help(MODULE)".""".format(sage.version.version))