passagemath-repl 10.4.62__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. passagemath_repl-10.4.62.data/scripts/sage-cachegrind +25 -0
  2. passagemath_repl-10.4.62.data/scripts/sage-callgrind +16 -0
  3. passagemath_repl-10.4.62.data/scripts/sage-cleaner +230 -0
  4. passagemath_repl-10.4.62.data/scripts/sage-coverage +327 -0
  5. passagemath_repl-10.4.62.data/scripts/sage-eval +14 -0
  6. passagemath_repl-10.4.62.data/scripts/sage-fixdoctests +708 -0
  7. passagemath_repl-10.4.62.data/scripts/sage-inline-fortran +12 -0
  8. passagemath_repl-10.4.62.data/scripts/sage-ipynb2rst +50 -0
  9. passagemath_repl-10.4.62.data/scripts/sage-ipython +16 -0
  10. passagemath_repl-10.4.62.data/scripts/sage-massif +25 -0
  11. passagemath_repl-10.4.62.data/scripts/sage-notebook +267 -0
  12. passagemath_repl-10.4.62.data/scripts/sage-omega +25 -0
  13. passagemath_repl-10.4.62.data/scripts/sage-preparse +302 -0
  14. passagemath_repl-10.4.62.data/scripts/sage-run +27 -0
  15. passagemath_repl-10.4.62.data/scripts/sage-run-cython +10 -0
  16. passagemath_repl-10.4.62.data/scripts/sage-runtests +9 -0
  17. passagemath_repl-10.4.62.data/scripts/sage-startuptime.py +163 -0
  18. passagemath_repl-10.4.62.data/scripts/sage-valgrind +34 -0
  19. passagemath_repl-10.4.62.dist-info/METADATA +77 -0
  20. passagemath_repl-10.4.62.dist-info/RECORD +162 -0
  21. passagemath_repl-10.4.62.dist-info/WHEEL +5 -0
  22. passagemath_repl-10.4.62.dist-info/top_level.txt +1 -0
  23. sage/all__sagemath_repl.py +119 -0
  24. sage/doctest/__init__.py +4 -0
  25. sage/doctest/__main__.py +236 -0
  26. sage/doctest/all.py +4 -0
  27. sage/doctest/check_tolerance.py +261 -0
  28. sage/doctest/control.py +1727 -0
  29. sage/doctest/external.py +534 -0
  30. sage/doctest/fixtures.py +383 -0
  31. sage/doctest/forker.py +2665 -0
  32. sage/doctest/marked_output.py +102 -0
  33. sage/doctest/parsing.py +1708 -0
  34. sage/doctest/parsing_test.py +79 -0
  35. sage/doctest/reporting.py +733 -0
  36. sage/doctest/rif_tol.py +124 -0
  37. sage/doctest/sources.py +1657 -0
  38. sage/doctest/test.py +584 -0
  39. sage/doctest/tests/1second.rst +4 -0
  40. sage/doctest/tests/99seconds.rst +4 -0
  41. sage/doctest/tests/abort.rst +5 -0
  42. sage/doctest/tests/atexit.rst +7 -0
  43. sage/doctest/tests/fail_and_die.rst +6 -0
  44. sage/doctest/tests/initial.rst +15 -0
  45. sage/doctest/tests/interrupt.rst +7 -0
  46. sage/doctest/tests/interrupt_diehard.rst +14 -0
  47. sage/doctest/tests/keyboardinterrupt.rst +11 -0
  48. sage/doctest/tests/longtime.rst +5 -0
  49. sage/doctest/tests/nodoctest +5 -0
  50. sage/doctest/tests/random_seed.rst +4 -0
  51. sage/doctest/tests/show_skipped.rst +18 -0
  52. sage/doctest/tests/sig_on.rst +9 -0
  53. sage/doctest/tests/simple_failure.rst +8 -0
  54. sage/doctest/tests/sleep_and_raise.rst +106 -0
  55. sage/doctest/tests/tolerance.rst +31 -0
  56. sage/doctest/util.py +750 -0
  57. sage/interfaces/cleaner.py +48 -0
  58. sage/interfaces/quit.py +163 -0
  59. sage/misc/all__sagemath_repl.py +51 -0
  60. sage/misc/banner.py +235 -0
  61. sage/misc/benchmark.py +221 -0
  62. sage/misc/classgraph.py +131 -0
  63. sage/misc/copying.py +22 -0
  64. sage/misc/cython.py +694 -0
  65. sage/misc/dev_tools.py +745 -0
  66. sage/misc/edit_module.py +304 -0
  67. sage/misc/explain_pickle.py +3079 -0
  68. sage/misc/gperftools.py +361 -0
  69. sage/misc/inline_fortran.py +212 -0
  70. sage/misc/messaging.py +86 -0
  71. sage/misc/pager.py +21 -0
  72. sage/misc/profiler.py +179 -0
  73. sage/misc/python.py +70 -0
  74. sage/misc/remote_file.py +53 -0
  75. sage/misc/sage_eval.py +246 -0
  76. sage/misc/sage_input.py +3621 -0
  77. sage/misc/sagedoc.py +1742 -0
  78. sage/misc/sh.py +38 -0
  79. sage/misc/trace.py +90 -0
  80. sage/repl/__init__.py +16 -0
  81. sage/repl/all.py +15 -0
  82. sage/repl/attach.py +625 -0
  83. sage/repl/configuration.py +186 -0
  84. sage/repl/display/__init__.py +1 -0
  85. sage/repl/display/fancy_repr.py +354 -0
  86. sage/repl/display/formatter.py +318 -0
  87. sage/repl/display/jsmol_iframe.py +290 -0
  88. sage/repl/display/pretty_print.py +153 -0
  89. sage/repl/display/util.py +163 -0
  90. sage/repl/image.py +302 -0
  91. sage/repl/inputhook.py +91 -0
  92. sage/repl/interface_magic.py +298 -0
  93. sage/repl/interpreter.py +854 -0
  94. sage/repl/ipython_extension.py +593 -0
  95. sage/repl/ipython_kernel/__init__.py +1 -0
  96. sage/repl/ipython_kernel/__main__.py +4 -0
  97. sage/repl/ipython_kernel/all_jupyter.py +10 -0
  98. sage/repl/ipython_kernel/install.py +301 -0
  99. sage/repl/ipython_kernel/interact.py +278 -0
  100. sage/repl/ipython_kernel/kernel.py +217 -0
  101. sage/repl/ipython_kernel/widgets.py +466 -0
  102. sage/repl/ipython_kernel/widgets_sagenb.py +587 -0
  103. sage/repl/ipython_tests.py +163 -0
  104. sage/repl/load.py +326 -0
  105. sage/repl/preparse.py +2218 -0
  106. sage/repl/prompts.py +90 -0
  107. sage/repl/rich_output/__init__.py +4 -0
  108. sage/repl/rich_output/backend_base.py +648 -0
  109. sage/repl/rich_output/backend_doctest.py +316 -0
  110. sage/repl/rich_output/backend_emacs.py +151 -0
  111. sage/repl/rich_output/backend_ipython.py +596 -0
  112. sage/repl/rich_output/buffer.py +311 -0
  113. sage/repl/rich_output/display_manager.py +829 -0
  114. sage/repl/rich_output/example.avi +0 -0
  115. sage/repl/rich_output/example.canvas3d +1 -0
  116. sage/repl/rich_output/example.dvi +0 -0
  117. sage/repl/rich_output/example.flv +0 -0
  118. sage/repl/rich_output/example.gif +0 -0
  119. sage/repl/rich_output/example.jpg +0 -0
  120. sage/repl/rich_output/example.mkv +0 -0
  121. sage/repl/rich_output/example.mov +0 -0
  122. sage/repl/rich_output/example.mp4 +0 -0
  123. sage/repl/rich_output/example.ogv +0 -0
  124. sage/repl/rich_output/example.pdf +0 -0
  125. sage/repl/rich_output/example.png +0 -0
  126. sage/repl/rich_output/example.svg +54 -0
  127. sage/repl/rich_output/example.webm +0 -0
  128. sage/repl/rich_output/example.wmv +0 -0
  129. sage/repl/rich_output/example_jmol.spt.zip +0 -0
  130. sage/repl/rich_output/example_wavefront_scene.mtl +7 -0
  131. sage/repl/rich_output/example_wavefront_scene.obj +17 -0
  132. sage/repl/rich_output/output_basic.py +391 -0
  133. sage/repl/rich_output/output_browser.py +103 -0
  134. sage/repl/rich_output/output_catalog.py +54 -0
  135. sage/repl/rich_output/output_graphics.py +320 -0
  136. sage/repl/rich_output/output_graphics3d.py +345 -0
  137. sage/repl/rich_output/output_video.py +231 -0
  138. sage/repl/rich_output/preferences.py +432 -0
  139. sage/repl/rich_output/pretty_print.py +339 -0
  140. sage/repl/rich_output/test_backend.py +201 -0
  141. sage/repl/user_globals.py +214 -0
  142. sage/tests/__init__.py +1 -0
  143. sage/tests/all.py +3 -0
  144. sage/tests/article_heuberger_krenn_kropf_fsm-in-sage.py +630 -0
  145. sage/tests/arxiv_0812_2725.py +351 -0
  146. sage/tests/benchmark.py +1923 -0
  147. sage/tests/book_schilling_zabrocki_kschur_primer.py +795 -0
  148. sage/tests/book_stein_ent.py +651 -0
  149. sage/tests/book_stein_modform.py +558 -0
  150. sage/tests/cmdline.py +790 -0
  151. sage/tests/combinatorial_hopf_algebras.py +52 -0
  152. sage/tests/finite_poset.py +623 -0
  153. sage/tests/functools_partial_src.py +27 -0
  154. sage/tests/gosper-sum.py +218 -0
  155. sage/tests/lazy_imports.py +28 -0
  156. sage/tests/modular_group_cohomology.py +80 -0
  157. sage/tests/numpy.py +21 -0
  158. sage/tests/parigp.py +76 -0
  159. sage/tests/startup.py +27 -0
  160. sage/tests/symbolic-series.py +76 -0
  161. sage/tests/sympy.py +16 -0
  162. sage/tests/test_deprecation.py +31 -0
@@ -0,0 +1,1657 @@
1
+ # sage_setup: distribution = sagemath-repl
2
+ """
3
+ Classes for sources of doctests
4
+
5
+ This module defines various classes for sources from which doctests
6
+ originate, such as files, functions or database entries.
7
+
8
+ AUTHORS:
9
+
10
+ - David Roe (2012-03-27) -- initial version, based on Robert Bradshaw's code.
11
+ """
12
+
13
+ # ****************************************************************************
14
+ # Copyright (C) 2012-2013 David Roe <roed.math@gmail.com>
15
+ # 2012 Robert Bradshaw <robertwb@gmail.com>
16
+ # 2012 William Stein <wstein@gmail.com>
17
+ # 2013 R. Andrew Ohana
18
+ # 2013-2017 Jeroen Demeyer <jdemeyer@cage.ugent.be>
19
+ # 2013-2019 John H. Palmieri
20
+ # 2014 Volker Braun
21
+ # 2014-2022 Frédéric Chapoton
22
+ # 2017 Erik M. Bray
23
+ # 2021 Sébastien Labbé
24
+ # 2021-2023 Matthias Koeppe
25
+ #
26
+ # Distributed under the terms of the GNU General Public License (GPL)
27
+ #
28
+ # https://www.gnu.org/licenses/
29
+ # ****************************************************************************
30
+
31
+ import os
32
+ import sys
33
+ import re
34
+ import random
35
+ import doctest
36
+ from sage.cpython.string import bytes_to_str
37
+ from sage.repl.load import load
38
+ from sage.misc.lazy_attribute import lazy_attribute
39
+ from sage.misc.package_dir import is_package_or_sage_namespace_package_dir
40
+ from .parsing import SageDocTestParser
41
+ from .util import NestedName
42
+ from sage.structure.dynamic_class import dynamic_class
43
+ from sage.env import SAGE_SRC, SAGE_LIB
44
+
45
+ # Python file parsing
46
+ triple_quotes = re.compile(r"\s*[rRuU]*((''')|(\"\"\"))")
47
+ name_regex = re.compile(r".*\s(\w+)([(].*)?:")
48
+
49
+ # LaTeX file parsing
50
+ begin_verb = re.compile(r"\s*\\begin{verbatim}")
51
+ end_verb = re.compile(r"\s*\\end{verbatim}\s*(%link)?")
52
+ begin_lstli = re.compile(r"\s*\\begin{lstlisting}")
53
+ end_lstli = re.compile(r"\s*\\end{lstlisting}\s*(%link)?")
54
+ skip = re.compile(r".*%skip.*")
55
+
56
+ # ReST file parsing
57
+ link_all = re.compile(r"^\s*\.\.\s+linkall\s*$")
58
+ double_colon = re.compile(r"^(\s*).*::\s*$")
59
+ code_block = re.compile(r"^(\s*)[.][.]\s*code-block\s*::.*$")
60
+
61
+ whitespace = re.compile(r"\s*")
62
+ bitness_marker = re.compile('#.*(32|64)-bit')
63
+ bitness_value = '64' if sys.maxsize > (1 << 32) else '32'
64
+
65
+ # For neutralizing doctests
66
+ find_prompt = re.compile(r"^(\s*)(>>>|sage:)(.*)")
67
+
68
+ # For testing that enough doctests are created
69
+ sagestart = re.compile(r"^(\s*(>>> |sage: ))\s*[^#\s]")
70
+ untested = re.compile("(not implemented|not tested)")
71
+
72
+ # For parsing a PEP 0263 encoding declaration
73
+ pep_0263 = re.compile(br'^[ \t\v]*#.*?coding[:=]\s*([-\w.]+)')
74
+
75
+ # Source line number in warning output
76
+ doctest_line_number = re.compile(r"^\s*doctest:[0-9]")
77
+
78
+
79
+ def get_basename(path):
80
+ """
81
+ This function returns the basename of the given path, e.g.
82
+ ``sage.doctest.sources`` or ``doc.ru.tutorial.tour_advanced``.
83
+
84
+ EXAMPLES::
85
+
86
+ sage: from sage.doctest.sources import get_basename
87
+ sage: import os
88
+ sage: get_basename(sage.doctest.sources.__file__)
89
+ 'sage.doctest.sources'
90
+ sage: get_basename(os.path.join(sage.structure.__path__[0], 'element.pxd'))
91
+ 'sage.structure.element.pxd'
92
+ """
93
+ if path is None:
94
+ return None
95
+ if not os.path.exists(path):
96
+ return path
97
+ path = os.path.abspath(path)
98
+ root = os.path.dirname(path)
99
+ # If the file is in the sage library, we can use our knowledge of
100
+ # the directory structure
101
+ dev = SAGE_SRC
102
+ sp = SAGE_LIB
103
+ if path.startswith(dev):
104
+ # there will be a branch name
105
+ i = path.find(os.path.sep, len(dev))
106
+ if i == -1:
107
+ # this source is the whole library....
108
+ return path
109
+ root = path[:i]
110
+ elif path.startswith(sp):
111
+ root = path[:len(sp)]
112
+ else:
113
+ # If this file is in some python package we can see how deep
114
+ # it goes.
115
+ while is_package_or_sage_namespace_package_dir(root):
116
+ root = os.path.dirname(root)
117
+ fully_qualified_path, ext = os.path.splitext(path[len(root) + 1:])
118
+ if os.path.split(path)[1] == '__init__.py':
119
+ fully_qualified_path = fully_qualified_path[:-9]
120
+ basename = fully_qualified_path.replace(os.path.sep, '.')
121
+ if ext in ['.pxd', '.pxi']:
122
+ # disambiguate from .pyx with the same basename
123
+ basename += ext
124
+ return basename
125
+
126
+
127
+ class DocTestSource:
128
+ """
129
+ This class provides a common base class for different sources of doctests.
130
+
131
+ INPUT:
132
+
133
+ - ``options`` -- a :class:`sage.doctest.control.DocTestDefaults`
134
+ instance or equivalent
135
+ """
136
+ def __init__(self, options):
137
+ """
138
+ Initialization.
139
+
140
+ EXAMPLES::
141
+
142
+ sage: from sage.doctest.control import DocTestDefaults
143
+ sage: from sage.doctest.sources import FileDocTestSource
144
+ sage: filename = sage.doctest.sources.__file__
145
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
146
+ sage: TestSuite(FDS).run()
147
+ """
148
+ self.options = options
149
+
150
+ def __eq__(self, other):
151
+ """
152
+ Comparison is just by comparison of attributes.
153
+
154
+ EXAMPLES::
155
+
156
+ sage: from sage.doctest.control import DocTestDefaults
157
+ sage: from sage.doctest.sources import FileDocTestSource
158
+ sage: filename = sage.doctest.sources.__file__
159
+ sage: DD = DocTestDefaults()
160
+ sage: FDS = FileDocTestSource(filename, DD)
161
+ sage: FDS2 = FileDocTestSource(filename, DD)
162
+ sage: FDS == FDS2
163
+ True
164
+ """
165
+ if type(self) is not type(other):
166
+ return False
167
+ return self.__dict__ == other.__dict__
168
+
169
+ def __ne__(self, other):
170
+ """
171
+ Test for non-equality.
172
+
173
+ EXAMPLES::
174
+
175
+ sage: from sage.doctest.control import DocTestDefaults
176
+ sage: from sage.doctest.sources import FileDocTestSource
177
+ sage: filename = sage.doctest.sources.__file__
178
+ sage: DD = DocTestDefaults()
179
+ sage: FDS = FileDocTestSource(filename, DD)
180
+ sage: FDS2 = FileDocTestSource(filename, DD)
181
+ sage: FDS != FDS2
182
+ False
183
+ """
184
+ return not (self == other)
185
+
186
+ def _process_doc(self, doctests, doc, namespace, start):
187
+ """
188
+ Appends doctests defined in ``doc`` to the list ``doctests``.
189
+
190
+ This function is called when a docstring block is completed
191
+ (either by ending a triple quoted string in a Python file,
192
+ unindenting from a comment block in a ReST file, or ending a
193
+ verbatim or lstlisting environment in a LaTeX file).
194
+
195
+ INPUT:
196
+
197
+ - ``doctests`` -- a running list of doctests to which the new
198
+ test(s) will be appended
199
+
200
+ - ``doc`` -- list of lines of a docstring, each including
201
+ the trailing newline
202
+
203
+ - ``namespace`` -- dictionary or
204
+ :class:`sage.doctest.util.RecordingDict`, used in the
205
+ creation of new :class:`doctest.DocTest` s
206
+
207
+ - ``start`` -- integer giving the line number of the start
208
+ of this docstring in the larger file
209
+
210
+ EXAMPLES::
211
+
212
+ sage: from sage.doctest.control import DocTestDefaults
213
+ sage: from sage.doctest.sources import FileDocTestSource
214
+ sage: from sage.doctest.parsing import SageDocTestParser
215
+ sage: filename = sage.doctest.util.__file__
216
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
217
+ sage: doctests, _ = FDS.create_doctests({})
218
+ sage: manual_doctests = []
219
+ sage: for dt in doctests:
220
+ ....: FDS.qualified_name = dt.name
221
+ ....: FDS._process_doc(manual_doctests, dt.docstring, {}, dt.lineno-1)
222
+ sage: doctests == manual_doctests
223
+ True
224
+ """
225
+ docstring = "".join(doc)
226
+ new_doctests = self.parse_docstring(docstring, namespace, start)
227
+ sig_on_count_doc_doctest = "sig_on_count() # check sig_on/off pairings (virtual doctest)\n"
228
+ for dt in new_doctests:
229
+ if len(dt.examples) > 0 and not (hasattr(dt.examples[-1], 'sage_source')
230
+ and dt.examples[-1].sage_source == sig_on_count_doc_doctest):
231
+ # Line number refers to the end of the docstring
232
+ sigon = doctest.Example(sig_on_count_doc_doctest, "0\n", lineno=docstring.count("\n"))
233
+ sigon.sage_source = sig_on_count_doc_doctest
234
+ sigon.optional_tags = frozenset(self.file_optional_tags)
235
+ sigon.probed_tags = frozenset()
236
+ dt.examples.append(sigon)
237
+ doctests.append(dt)
238
+
239
+ @lazy_attribute
240
+ def file_optional_tags(self):
241
+ r"""
242
+ Return the set of tags that should apply to all doctests in this source.
243
+
244
+ This default implementation just returns the empty set.
245
+
246
+ EXAMPLES::
247
+
248
+ sage: from sage.doctest.control import DocTestDefaults
249
+ sage: from sage.doctest.sources import StringDocTestSource, PythonSource
250
+ sage: from sage.structure.dynamic_class import dynamic_class
251
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
252
+ sage: PythonStringSource = dynamic_class('PythonStringSource', (StringDocTestSource, PythonSource))
253
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
254
+ sage: PSS.file_optional_tags
255
+ set()
256
+ """
257
+ return set()
258
+
259
+ def _create_doctests(self, namespace, tab_okay=None):
260
+ """
261
+ Create a list of doctests defined in this source.
262
+
263
+ This function collects functionality common to file and string
264
+ sources, and is called by
265
+ :meth:`FileDocTestSource.create_doctests`.
266
+
267
+ INPUT:
268
+
269
+ - ``namespace`` -- dictionary or
270
+ :class:`sage.doctest.util.RecordingDict`, used in the
271
+ creation of new :class:`doctest.DocTest` s.
272
+
273
+ - ``tab_okay`` -- whether tabs are allowed in this source
274
+
275
+ OUTPUT:
276
+
277
+ - ``doctests`` -- list of doctests defined by this source
278
+
279
+ - ``extras`` -- dictionary with ``extras['tab']`` either
280
+ ``False`` or a list of linenumbers on which tabs appear
281
+
282
+ EXAMPLES::
283
+
284
+ sage: from sage.doctest.control import DocTestDefaults
285
+ sage: from sage.doctest.sources import FileDocTestSource
286
+ sage: from sage.doctest.util import NestedName
287
+ sage: filename = sage.doctest.sources.__file__
288
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
289
+ sage: FDS.qualified_name = NestedName('sage.doctest.sources')
290
+ sage: doctests, extras = FDS._create_doctests({})
291
+ sage: len(doctests)
292
+ 43
293
+ sage: extras['tab']
294
+ False
295
+ sage: extras['line_number']
296
+ False
297
+ """
298
+ if tab_okay is None:
299
+ tab_okay = isinstance(self, TexSource)
300
+ self._init()
301
+ self.line_shift = 0
302
+ self.parser = SageDocTestParser(self.options.optional,
303
+ self.options.long,
304
+ probed_tags=self.options.probe,
305
+ file_optional_tags=self.file_optional_tags)
306
+ self.linking = False
307
+ doctests = []
308
+ in_docstring = False
309
+ unparsed_doc = False
310
+ doc = []
311
+ start = None
312
+ tab_locations = []
313
+ contains_line_number = False
314
+ for lineno, line in self:
315
+ if doctest_line_number.search(line) is not None:
316
+ contains_line_number = True
317
+ if "\t" in line:
318
+ tab_locations.append(str(lineno+1))
319
+ if "SAGE_DOCTEST_ALLOW_TABS" in line:
320
+ tab_okay = True
321
+ just_finished = False
322
+ if in_docstring:
323
+ if self.ending_docstring(line):
324
+ in_docstring = False
325
+ just_finished = True
326
+ self._process_doc(doctests, doc, namespace, start)
327
+ unparsed_doc = False
328
+ else:
329
+ bitness = bitness_marker.search(line)
330
+ if bitness:
331
+ if bitness.groups()[0] != bitness_value:
332
+ self.line_shift += 1
333
+ continue
334
+ else:
335
+ line = line[:bitness.start()] + "\n"
336
+ if self.line_shift and (m := sagestart.match(line)):
337
+ # We insert empty doctest lines to make up for the removed lines
338
+ indent_and_prompt = m.group(1)
339
+ doc.extend([indent_and_prompt + "# inserted to compensate for removed conditional doctest output\n"]
340
+ * self.line_shift)
341
+ self.line_shift = 0
342
+ doc.append(line)
343
+ unparsed_doc = True
344
+ if not in_docstring and (not just_finished or self.start_finish_can_overlap):
345
+ # to get line numbers in linked docstrings correct we
346
+ # append a blank line to the doc list.
347
+ doc.append("\n")
348
+ if not line.strip():
349
+ continue
350
+ if self.starting_docstring(line):
351
+ in_docstring = True
352
+ if self.linking:
353
+ # If there's already a doctest, we overwrite it.
354
+ if len(doctests) > 0:
355
+ doctests.pop()
356
+ if start is None:
357
+ start = lineno
358
+ doc = []
359
+ else:
360
+ self.line_shift = 0
361
+ start = lineno
362
+ doc = []
363
+ # In ReST files we can end the file without decreasing the indentation level.
364
+ if unparsed_doc:
365
+ self._process_doc(doctests, doc, namespace, start)
366
+
367
+ extras = {"tab": not tab_okay and tab_locations,
368
+ "line_number": contains_line_number,
369
+ "optionals": self.parser.optionals}
370
+ if self.options.randorder is not None and self.options.randorder is not False:
371
+ # we want to randomize even when self.randorder = 0
372
+ random.seed(self.options.randorder)
373
+ randomized = []
374
+ while doctests:
375
+ i = random.randint(0, len(doctests) - 1)
376
+ randomized.append(doctests.pop(i))
377
+ return randomized, extras
378
+ else:
379
+ return doctests, extras
380
+
381
+
382
+ class StringDocTestSource(DocTestSource):
383
+ r"""
384
+ This class creates doctests from a string.
385
+
386
+ INPUT:
387
+
388
+ - ``basename`` -- string such as 'sage.doctests.sources', going
389
+ into the names of created doctests and examples
390
+
391
+ - ``source`` -- string, giving the source code to be parsed for
392
+ doctests
393
+
394
+ - ``options`` -- a :class:`sage.doctest.control.DocTestDefaults`
395
+ or equivalent
396
+
397
+ - ``printpath`` -- string, to be used in place of a filename
398
+ when doctest failures are displayed
399
+
400
+ - ``lineno_shift`` -- integer (default: 0) by which to shift
401
+ the line numbers of all doctests defined in this string
402
+
403
+ EXAMPLES::
404
+
405
+ sage: from sage.doctest.control import DocTestDefaults
406
+ sage: from sage.doctest.sources import StringDocTestSource, PythonSource
407
+ sage: from sage.structure.dynamic_class import dynamic_class
408
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
409
+ sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource))
410
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
411
+ sage: dt, extras = PSS.create_doctests({})
412
+ sage: len(dt)
413
+ 1
414
+ sage: extras['tab']
415
+ []
416
+ sage: extras['line_number']
417
+ False
418
+
419
+ sage: s = "'''\n\tsage: 2 + 2\n\t4\n'''"
420
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
421
+ sage: dt, extras = PSS.create_doctests({})
422
+ sage: extras['tab']
423
+ ['2', '3']
424
+
425
+ sage: s = "'''\n sage: import warnings; warnings.warn('foo')\n doctest:1: UserWarning: foo \n'''"
426
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
427
+ sage: dt, extras = PSS.create_doctests({})
428
+ sage: extras['line_number']
429
+ True
430
+ """
431
+ def __init__(self, basename, source, options, printpath, lineno_shift=0):
432
+ r"""
433
+ Initialization.
434
+
435
+ TESTS::
436
+
437
+ sage: from sage.doctest.control import DocTestDefaults
438
+ sage: from sage.doctest.sources import StringDocTestSource, PythonSource
439
+ sage: from sage.structure.dynamic_class import dynamic_class
440
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
441
+ sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource))
442
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
443
+ sage: TestSuite(PSS).run()
444
+ """
445
+ self.qualified_name = NestedName(basename)
446
+ self.printpath = printpath
447
+ self.source = source
448
+ self.lineno_shift = lineno_shift
449
+ DocTestSource.__init__(self, options)
450
+
451
+ def __iter__(self):
452
+ r"""
453
+ Iterating over this source yields pairs ``(lineno, line)``.
454
+
455
+ EXAMPLES::
456
+
457
+ sage: from sage.doctest.control import DocTestDefaults
458
+ sage: from sage.doctest.sources import StringDocTestSource, PythonSource
459
+ sage: from sage.structure.dynamic_class import dynamic_class
460
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
461
+ sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource))
462
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
463
+ sage: for n, line in PSS:
464
+ ....: print("{} {}".format(n, line))
465
+ 0 '''
466
+ 1 sage: 2 + 2
467
+ 2 4
468
+ 3 '''
469
+ """
470
+ for lineno, line in enumerate(self.source.split('\n')):
471
+ yield lineno + self.lineno_shift, line + '\n'
472
+
473
+ def create_doctests(self, namespace):
474
+ r"""
475
+ Create doctests from this string.
476
+
477
+ INPUT:
478
+
479
+ - ``namespace`` -- dictionary or :class:`sage.doctest.util.RecordingDict`
480
+
481
+ OUTPUT:
482
+
483
+ - ``doctests`` -- list of doctests defined by this string
484
+
485
+ - ``tab_locations`` -- either ``False`` or a list of linenumbers
486
+ on which tabs appear
487
+
488
+ EXAMPLES::
489
+
490
+ sage: from sage.doctest.control import DocTestDefaults
491
+ sage: from sage.doctest.sources import StringDocTestSource, PythonSource
492
+ sage: from sage.structure.dynamic_class import dynamic_class
493
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
494
+ sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource))
495
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
496
+ sage: dt, tabs = PSS.create_doctests({})
497
+ sage: for t in dt:
498
+ ....: print("{} {}".format(t.name, t.examples[0].sage_source))
499
+ <runtime> 2 + 2
500
+ """
501
+ return self._create_doctests(namespace)
502
+
503
+
504
+ class FileDocTestSource(DocTestSource):
505
+ """
506
+ This class creates doctests from a file.
507
+
508
+ INPUT:
509
+
510
+ - ``path`` -- string; the filename
511
+
512
+ - ``options`` -- a :class:`sage.doctest.control.DocTestDefaults`
513
+ instance or equivalent
514
+
515
+ EXAMPLES::
516
+
517
+ sage: from sage.doctest.control import DocTestDefaults
518
+ sage: from sage.doctest.sources import FileDocTestSource
519
+ sage: filename = sage.doctest.sources.__file__
520
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
521
+ sage: FDS.basename
522
+ 'sage.doctest.sources'
523
+
524
+ TESTS::
525
+
526
+ sage: TestSuite(FDS).run()
527
+
528
+ ::
529
+
530
+ sage: from sage.doctest.control import DocTestDefaults
531
+ sage: from sage.doctest.sources import FileDocTestSource
532
+ sage: filename = tmp_filename(ext='.txtt')
533
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
534
+ Traceback (most recent call last):
535
+ ...
536
+ ValueError: unknown extension for the file to test (=...txtt),
537
+ valid extensions are: .py, .pyx, .pxd, .pxi, .sage, .spyx, .tex, .rst, .rst.txt
538
+ """
539
+ def __init__(self, path, options):
540
+ """
541
+ Initialization.
542
+
543
+ EXAMPLES::
544
+
545
+ sage: from sage.doctest.control import DocTestDefaults
546
+ sage: from sage.doctest.sources import FileDocTestSource
547
+ sage: filename = sage.doctest.sources.__file__
548
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults(randorder=0))
549
+ sage: FDS.options.randorder
550
+ 0
551
+ """
552
+ self.path = path
553
+ DocTestSource.__init__(self, options)
554
+ if path.endswith('.rst.txt'):
555
+ ext = '.rst.txt'
556
+ else:
557
+ base, ext = os.path.splitext(path)
558
+ valid_code_ext = ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx')
559
+ if ext in valid_code_ext:
560
+ self.__class__ = dynamic_class('PythonFileSource', (FileDocTestSource, PythonSource))
561
+ self.encoding = "utf-8"
562
+ elif ext == '.tex':
563
+ self.__class__ = dynamic_class('TexFileSource', (FileDocTestSource, TexSource))
564
+ self.encoding = "utf-8"
565
+ elif ext == '.rst' or ext == '.rst.txt':
566
+ self.__class__ = dynamic_class('RestFileSource', (FileDocTestSource, RestSource))
567
+ self.encoding = "utf-8"
568
+ else:
569
+ valid_ext = ", ".join(valid_code_ext + ('.tex', '.rst', '.rst.txt'))
570
+ raise ValueError("unknown extension for the file to test (={}),"
571
+ " valid extensions are: {}".format(path, valid_ext))
572
+
573
+ def __iter__(self):
574
+ r"""
575
+ Iterating over this source yields pairs ``(lineno, line)``.
576
+
577
+ EXAMPLES::
578
+
579
+ sage: from sage.doctest.control import DocTestDefaults
580
+ sage: from sage.doctest.sources import FileDocTestSource
581
+ sage: filename = tmp_filename(ext='.py')
582
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
583
+ sage: with open(filename, 'w') as f:
584
+ ....: _ = f.write(s)
585
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
586
+ sage: for n, line in FDS:
587
+ ....: print("{} {}".format(n, line))
588
+ 0 '''
589
+ 1 sage: 2 + 2
590
+ 2 4
591
+ 3 '''
592
+
593
+ The encoding is "utf-8" by default::
594
+
595
+ sage: FDS.encoding
596
+ 'utf-8'
597
+
598
+ We create a file with a Latin-1 encoding without declaring it::
599
+
600
+ sage: s = b"'''\nRegardons le polyn\xF4me...\n'''\n"
601
+ sage: with open(filename, 'wb') as f:
602
+ ....: _ = f.write(s)
603
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
604
+ sage: L = list(FDS)
605
+ Traceback (most recent call last):
606
+ ...
607
+ UnicodeDecodeError: 'utf...8' codec can...t decode byte 0xf4 in position 18: invalid continuation byte
608
+
609
+ This works if we add a PEP 0263 encoding declaration::
610
+
611
+ sage: s = b"#!/usr/bin/env python\n# -*- coding: latin-1 -*-\n" + s
612
+ sage: with open(filename, 'wb') as f:
613
+ ....: _ = f.write(s)
614
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
615
+ sage: L = list(FDS)
616
+ sage: FDS.encoding
617
+ 'latin-1'
618
+ """
619
+ with open(self.path, 'rb') as source:
620
+ for lineno, line in enumerate(source):
621
+ if lineno < 2:
622
+ match = pep_0263.search(line)
623
+ if match:
624
+ self.encoding = bytes_to_str(match.group(1), 'ascii')
625
+ yield lineno, line.decode(self.encoding)
626
+
627
+ @lazy_attribute
628
+ def printpath(self):
629
+ """
630
+ Whether the path is printed absolutely or relatively depends on an option.
631
+
632
+ EXAMPLES::
633
+
634
+ sage: from sage.doctest.control import DocTestDefaults
635
+ sage: from sage.doctest.sources import FileDocTestSource
636
+ sage: import os
637
+ sage: filename = os.path.realpath(sage.doctest.sources.__file__)
638
+ sage: root = os.path.join(os.path.dirname(filename), '..')
639
+ sage: cwd = os.getcwd()
640
+ sage: os.chdir(root)
641
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults(randorder=0,
642
+ ....: abspath=False))
643
+ sage: FDS.printpath
644
+ 'doctest/sources.py'
645
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults(randorder=0,
646
+ ....: abspath=True))
647
+ sage: FDS.printpath
648
+ '.../sage/doctest/sources.py'
649
+ sage: os.chdir(cwd)
650
+ """
651
+ if self.options.abspath:
652
+ return os.path.abspath(self.path)
653
+ else:
654
+ relpath = os.path.relpath(self.path)
655
+ if relpath.startswith(".." + os.path.sep):
656
+ return self.path
657
+ else:
658
+ return relpath
659
+
660
+ @lazy_attribute
661
+ def basename(self):
662
+ """
663
+ The basename of this file source, e.g. ``sage.doctest.sources``.
664
+
665
+ EXAMPLES::
666
+
667
+ sage: from sage.doctest.control import DocTestDefaults
668
+ sage: from sage.doctest.sources import FileDocTestSource
669
+ sage: filename = os.path.join(SAGE_SRC,'sage','rings','integer.pyx')
670
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
671
+ sage: FDS.basename
672
+ 'sage.rings.integer'
673
+ """
674
+ return get_basename(self.path)
675
+
676
+ @lazy_attribute
677
+ def in_lib(self):
678
+ """
679
+ Whether this file is to be treated as a module in a Python package.
680
+
681
+ Such files aren't loaded before running tests.
682
+
683
+ This uses :func:`~sage.misc.package_dir.is_package_or_sage_namespace_package_dir`
684
+ but can be overridden via :class:`~sage.doctest.control.DocTestDefaults`.
685
+
686
+ EXAMPLES::
687
+
688
+ sage: from sage.doctest.control import DocTestDefaults
689
+ sage: from sage.doctest.sources import FileDocTestSource
690
+ sage: import os
691
+ sage: filename = os.path.join(SAGE_SRC, 'sage', 'rings', 'integer.pyx')
692
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
693
+ sage: FDS.in_lib
694
+ True
695
+ sage: filename = os.path.join(SAGE_SRC, 'sage', 'doctest', 'tests', 'abort.rst')
696
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
697
+ sage: FDS.in_lib
698
+ False
699
+
700
+ You can override the default::
701
+
702
+ sage: FDS = FileDocTestSource("hello_world.py", DocTestDefaults())
703
+ sage: FDS.in_lib
704
+ False
705
+ sage: FDS = FileDocTestSource("hello_world.py", DocTestDefaults(force_lib=True))
706
+ sage: FDS.in_lib
707
+ True
708
+ """
709
+ return (self.options.force_lib
710
+ or is_package_or_sage_namespace_package_dir(os.path.dirname(self.path)))
711
+
712
+ @lazy_attribute
713
+ def file_optional_tags(self):
714
+ """
715
+ Return the set of tags that should apply to all doctests in this source.
716
+
717
+ EXAMPLES::
718
+
719
+ sage: from sage.doctest.control import DocTestDefaults
720
+ sage: from sage.doctest.sources import FileDocTestSource
721
+ sage: filename = sage.repl.user_globals.__file__
722
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
723
+ sage: FDS.file_optional_tags
724
+ {'sage.modules': None}
725
+ """
726
+ from .parsing import parse_file_optional_tags
727
+ return parse_file_optional_tags(self)
728
+
729
+ def create_doctests(self, namespace):
730
+ r"""
731
+ Return a list of doctests for this file.
732
+
733
+ INPUT:
734
+
735
+ - ``namespace`` -- dictionary or :class:`sage.doctest.util.RecordingDict`
736
+
737
+ OUTPUT:
738
+
739
+ - ``doctests`` -- list of doctests defined in this file
740
+
741
+ - ``extras`` -- dictionary
742
+
743
+ EXAMPLES::
744
+
745
+ sage: from sage.doctest.control import DocTestDefaults
746
+ sage: from sage.doctest.sources import FileDocTestSource
747
+ sage: filename = sage.doctest.sources.__file__
748
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
749
+ sage: doctests, extras = FDS.create_doctests(globals())
750
+ sage: len(doctests)
751
+ 43
752
+ sage: extras['tab']
753
+ False
754
+
755
+ We give a self referential example::
756
+
757
+ sage: doctests[20].name
758
+ 'sage.doctest.sources.FileDocTestSource.create_doctests'
759
+ sage: doctests[20].examples[8].source
760
+ 'doctests[Integer(20)].examples[Integer(8)].source\n'
761
+
762
+ TESTS:
763
+
764
+ We check that we correctly process results that depend on 32
765
+ vs 64 bit architecture::
766
+
767
+ sage: import sys
768
+ sage: bitness = '64' if sys.maxsize > (1 << 32) else '32'
769
+ sage: gp.get_precision() == 38 # needs sage.libs.pari
770
+ False # 32-bit
771
+ True # 64-bit
772
+ sage: ex = doctests[20].examples[11]
773
+ sage: ((bitness == '64' and ex.want == 'True \n') # needs sage.libs.pari
774
+ ....: or (bitness == '32' and ex.want == 'False \n'))
775
+ True
776
+
777
+ We check that lines starting with a # aren't doctested::
778
+
779
+ #sage: raise RuntimeError
780
+ """
781
+ if not os.path.exists(self.path):
782
+ import errno
783
+ raise OSError(errno.ENOENT, "File does not exist", self.path)
784
+ base, filename = os.path.split(self.path)
785
+ _, ext = os.path.splitext(filename)
786
+ if not self.in_lib and ext in ('.py', '.pyx', '.sage', '.spyx'):
787
+ cwd = os.getcwd()
788
+ if base:
789
+ os.chdir(base)
790
+ try:
791
+ load(filename, namespace) # errors raised here will be caught in DocTestTask
792
+ finally:
793
+ os.chdir(cwd)
794
+ self.qualified_name = NestedName(self.basename)
795
+ return self._create_doctests(namespace)
796
+
797
+ def _test_enough_doctests(self, check_extras=True, verbose=True):
798
+ r"""
799
+ This function checks to see that the doctests are not getting
800
+ unexpectedly skipped.
801
+
802
+ It uses a different (and simpler) code path than the doctest
803
+ creation functions. In particular, it does not understand
804
+ file-level and block-level # optional / needs tags.
805
+
806
+ INPUT:
807
+
808
+ - ``check_extras`` -- boolean (default: ``True``); whether to check if
809
+ doctests are created that do not correspond to either a ``sage:``
810
+ or a ``>>>`` prompt
811
+
812
+ - ``verbose`` -- boolean (default: ``True``); whether to print
813
+ offending line numbers when there are missing or extra tests
814
+
815
+ TESTS::
816
+
817
+ sage: # not tested (because the output will change when source files are changed)
818
+ sage: from sage.doctest.control import DocTestDefaults
819
+ sage: from sage.doctest.sources import FileDocTestSource
820
+ sage: cwd = os.getcwd()
821
+ sage: os.chdir(SAGE_SRC)
822
+ sage: import itertools
823
+ sage: for path, dirs, files in itertools.chain(os.walk('sage'), os.walk('doc')):
824
+ ....: path = os.path.relpath(path)
825
+ ....: dirs.sort(); files.sort()
826
+ ....: for F in files:
827
+ ....: _, ext = os.path.splitext(F)
828
+ ....: if ext in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst'):
829
+ ....: filename = os.path.join(path, F)
830
+ ....: FDS = FileDocTestSource(filename, DocTestDefaults(long=True, optional=True, force_lib=True))
831
+ ....: FDS._test_enough_doctests(verbose=False)
832
+ There are 4 unexpected tests being run in sage/doctest/parsing.py
833
+ There are 1 unexpected tests being run in sage/doctest/reporting.py
834
+ sage: os.chdir(cwd)
835
+ """
836
+ expected = []
837
+ rest = isinstance(self, RestSource)
838
+ if rest:
839
+ skipping = False
840
+ in_block = False
841
+ last_line = ''
842
+ for lineno, line in self:
843
+ if not line.strip():
844
+ continue
845
+ if rest:
846
+ if line.strip().startswith(".. nodoctest"):
847
+ return
848
+ # We need to track blocks in order to figure out whether we're skipping.
849
+ if in_block:
850
+ indent = whitespace.match(line).end()
851
+ if indent <= starting_indent:
852
+ in_block = False
853
+ skipping = False
854
+ if not in_block:
855
+ m1 = double_colon.match(line)
856
+ m2 = code_block.match(line.lower())
857
+ starting = (m1 and not line.strip().startswith(".. ")) or m2
858
+ if starting:
859
+ if ".. skip" in last_line:
860
+ skipping = True
861
+ in_block = True
862
+ starting_indent = whitespace.match(line).end()
863
+ last_line = line
864
+ if (not rest or in_block) and sagestart.match(line) and not ((rest and skipping) or untested.search(line.lower())):
865
+ expected.append(lineno+1)
866
+ actual = []
867
+ tests, _ = self.create_doctests({})
868
+ for dt in tests:
869
+ if dt.examples:
870
+ for ex in dt.examples[:-1]: # the last entry is a sig_on_count()
871
+ actual.append(dt.lineno + ex.lineno + 1)
872
+ shortfall = sorted(set(expected).difference(set(actual)))
873
+ extras = sorted(set(actual).difference(set(expected)))
874
+ if len(actual) == len(expected):
875
+ if not shortfall:
876
+ return
877
+ dif = extras[0] - shortfall[0]
878
+ for e, s in zip(extras[1:],shortfall[1:]):
879
+ if dif != e - s:
880
+ break
881
+ else:
882
+ print("There are %s tests in %s that are shifted by %s" % (len(shortfall), self.path, dif))
883
+ if verbose:
884
+ print(" The correct line numbers are %s" % (", ".join(str(n) for n in shortfall)))
885
+ return
886
+ elif len(actual) < len(expected):
887
+ print("There are %s tests in %s that are not being run" % (len(expected) - len(actual), self.path))
888
+ elif check_extras:
889
+ print("There are %s unexpected tests being run in %s" % (len(actual) - len(expected), self.path))
890
+ if verbose:
891
+ if shortfall:
892
+ print(" Tests on lines %s are not run" % (", ".join(str(n) for n in shortfall)))
893
+ if check_extras and extras:
894
+ print(" Tests on lines %s seem extraneous" % (", ".join(str(n) for n in extras)))
895
+
896
+
897
+ class SourceLanguage:
898
+ """
899
+ An abstract class for functions that depend on the programming language of a doctest source.
900
+
901
+ Currently supported languages include Python, ReST and LaTeX.
902
+ """
903
+ def parse_docstring(self, docstring, namespace, start):
904
+ """
905
+ Return a list of doctest defined in this docstring.
906
+
907
+ This function is called by :meth:`DocTestSource._process_doc`.
908
+ The default implementation, defined here, is to use the
909
+ :class:`sage.doctest.parsing.SageDocTestParser` attached to
910
+ this source to get doctests from the docstring.
911
+
912
+ INPUT:
913
+
914
+ - ``docstring`` -- string containing documentation and tests
915
+
916
+ - ``namespace`` -- dictionary or :class:`sage.doctest.util.RecordingDict`
917
+
918
+ - ``start`` -- integer; one less than the starting line number
919
+
920
+ EXAMPLES::
921
+
922
+ sage: from sage.doctest.control import DocTestDefaults
923
+ sage: from sage.doctest.sources import FileDocTestSource
924
+ sage: from sage.doctest.parsing import SageDocTestParser
925
+ sage: from sage.doctest.util import NestedName
926
+ sage: filename = sage.doctest.util.__file__
927
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
928
+ sage: doctests, _ = FDS.create_doctests({})
929
+ sage: for dt in doctests:
930
+ ....: FDS.qualified_name = dt.name
931
+ ....: dt.examples = dt.examples[:-1] # strip off the sig_on() test
932
+ ....: assert(FDS.parse_docstring(dt.docstring,{},dt.lineno-1)[0] == dt)
933
+ """
934
+ return [self.parser.get_doctest(docstring, namespace, str(self.qualified_name),
935
+ self.printpath, start + 1)]
936
+
937
+
938
+ class PythonSource(SourceLanguage):
939
+ """
940
+ This class defines the functions needed for the extraction of doctests from python sources.
941
+
942
+ EXAMPLES::
943
+
944
+ sage: from sage.doctest.control import DocTestDefaults
945
+ sage: from sage.doctest.sources import FileDocTestSource
946
+ sage: filename = sage.doctest.sources.__file__
947
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
948
+ sage: type(FDS)
949
+ <class 'sage.doctest.sources.PythonFileSource'>
950
+ """
951
+ # The same line can't both start and end a docstring
952
+ start_finish_can_overlap = False
953
+
954
+ def _init(self):
955
+ """
956
+ This function is called before creating doctests from a Python source.
957
+
958
+ EXAMPLES::
959
+
960
+ sage: from sage.doctest.control import DocTestDefaults
961
+ sage: from sage.doctest.sources import FileDocTestSource
962
+ sage: filename = sage.doctest.sources.__file__
963
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
964
+ sage: FDS._init()
965
+ sage: FDS.last_indent
966
+ -1
967
+ """
968
+ self.last_indent = -1
969
+ self.last_line = None
970
+ self.quotetype = None
971
+ self.paren_count = 0
972
+ self.bracket_count = 0
973
+ self.curly_count = 0
974
+ self.code_wrapping = False
975
+
976
+ def _update_quotetype(self, line):
977
+ r"""
978
+ Update the track of what kind of quoted string we are in.
979
+
980
+ We need to track whether we're inside a triple quoted
981
+ string, since a triple quoted string that starts a line
982
+ could be the end of a string and thus not the beginning of a
983
+ doctest (see sage.misc.sageinspect for an example).
984
+
985
+ To do this tracking we need to track whether we're inside a
986
+ string at all, since ''' inside a string doesn't start a
987
+ triple quote (see the top of this file for an example).
988
+
989
+ We also need to track parentheses and brackets, since we only
990
+ want to update our record of last line and indentation level
991
+ when the line is actually over.
992
+
993
+ EXAMPLES::
994
+
995
+ sage: from sage.doctest.control import DocTestDefaults
996
+ sage: from sage.doctest.sources import FileDocTestSource
997
+ sage: filename = sage.doctest.sources.__file__
998
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
999
+ sage: FDS._init()
1000
+ sage: FDS._update_quotetype('\"\"\"'); print(" ".join(list(FDS.quotetype)))
1001
+ " " "
1002
+ sage: FDS._update_quotetype("'''"); print(" ".join(list(FDS.quotetype)))
1003
+ " " "
1004
+ sage: FDS._update_quotetype('\"\"\"'); print(FDS.quotetype)
1005
+ None
1006
+ sage: FDS._update_quotetype("triple_quotes = re.compile(\"\\s*[rRuU]*((''')|(\\\"\\\"\\\"))\")")
1007
+ sage: print(FDS.quotetype)
1008
+ None
1009
+ sage: FDS._update_quotetype("''' Single line triple quoted string \\''''")
1010
+ sage: print(FDS.quotetype)
1011
+ None
1012
+ sage: FDS._update_quotetype("' Lots of \\\\\\\\'")
1013
+ sage: print(FDS.quotetype)
1014
+ None
1015
+ """
1016
+ def _update_parens(start, end=None):
1017
+ self.paren_count += line.count("(",start,end) - line.count(")",start,end)
1018
+ self.bracket_count += line.count("[",start,end) - line.count("]",start,end)
1019
+ self.curly_count += line.count("{",start,end) - line.count("}",start,end)
1020
+ pos = 0
1021
+ while pos < len(line):
1022
+ if self.quotetype is None:
1023
+ next_single = line.find("'",pos)
1024
+ next_double = line.find('"',pos)
1025
+ if next_single == -1 and next_double == -1:
1026
+ next_comment = line.find("#",pos)
1027
+ if next_comment == -1:
1028
+ _update_parens(pos)
1029
+ else:
1030
+ _update_parens(pos,next_comment)
1031
+ break
1032
+ elif next_single == -1:
1033
+ m = next_double
1034
+ elif next_double == -1:
1035
+ m = next_single
1036
+ else:
1037
+ m = min(next_single, next_double)
1038
+ next_comment = line.find('#',pos,m)
1039
+ if next_comment != -1:
1040
+ _update_parens(pos,next_comment)
1041
+ break
1042
+ _update_parens(pos,m)
1043
+ if m+2 < len(line) and line[m] == line[m+1] == line[m+2]:
1044
+ self.quotetype = line[m:m+3]
1045
+ pos = m+3
1046
+ else:
1047
+ self.quotetype = line[m]
1048
+ pos = m+1
1049
+ else:
1050
+ next = line.find(self.quotetype,pos)
1051
+ if next == -1:
1052
+ break
1053
+ elif next == 0 or line[next-1] != '\\':
1054
+ pos = next + len(self.quotetype)
1055
+ self.quotetype = None
1056
+ else:
1057
+ # We need to worry about the possibility that
1058
+ # there are an even number of backslashes before
1059
+ # the quote, in which case it is not escaped
1060
+ count = 1
1061
+ slashpos = next - 2
1062
+ while slashpos >= pos and line[slashpos] == '\\':
1063
+ count += 1
1064
+ slashpos -= 1
1065
+ if count % 2 == 0:
1066
+ pos = next + len(self.quotetype)
1067
+ self.quotetype = None
1068
+ else:
1069
+ # The possible ending quote was escaped.
1070
+ pos = next + 1
1071
+
1072
+ def starting_docstring(self, line):
1073
+ """
1074
+ Determines whether the input line starts a docstring.
1075
+
1076
+ If the input line does start a docstring (a triple quote),
1077
+ then this function updates ``self.qualified_name``.
1078
+
1079
+ INPUT:
1080
+
1081
+ - ``line`` -- string; one line of an input file
1082
+
1083
+ OUTPUT: either ``None`` or a Match object
1084
+
1085
+ EXAMPLES::
1086
+
1087
+ sage: from sage.doctest.control import DocTestDefaults
1088
+ sage: from sage.doctest.sources import FileDocTestSource
1089
+ sage: from sage.doctest.util import NestedName
1090
+ sage: filename = sage.doctest.sources.__file__
1091
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1092
+ sage: FDS._init()
1093
+ sage: FDS.starting_docstring("r'''")
1094
+ <...Match object...>
1095
+ sage: FDS.ending_docstring("'''")
1096
+ <...Match object...>
1097
+ sage: FDS.qualified_name = NestedName(FDS.basename)
1098
+ sage: FDS.starting_docstring("class MyClass():")
1099
+ sage: FDS.starting_docstring(" def hello_world(self):")
1100
+ sage: FDS.starting_docstring(" '''")
1101
+ <...Match object...>
1102
+ sage: FDS.qualified_name
1103
+ sage.doctest.sources.MyClass.hello_world
1104
+ sage: FDS.ending_docstring(" '''")
1105
+ <...Match object...>
1106
+ sage: FDS.starting_docstring("class NewClass():")
1107
+ sage: FDS.starting_docstring(" '''")
1108
+ <...Match object...>
1109
+ sage: FDS.ending_docstring(" '''")
1110
+ <...Match object...>
1111
+ sage: FDS.qualified_name
1112
+ sage.doctest.sources.NewClass
1113
+ sage: FDS.starting_docstring("print(")
1114
+ sage: FDS.starting_docstring(" '''Not a docstring")
1115
+ sage: FDS.starting_docstring(" ''')")
1116
+ sage: FDS.starting_docstring("def foo():")
1117
+ sage: FDS.starting_docstring(" '''This is a docstring'''")
1118
+ <...Match object...>
1119
+ """
1120
+ indent = whitespace.match(line).end()
1121
+ quotematch = None
1122
+ if self.quotetype is None and not self.code_wrapping:
1123
+ # We're not inside a triple quote and not inside code like
1124
+ # print(
1125
+ # """Not a docstring
1126
+ # """)
1127
+
1128
+ if line[indent] != '#' and (indent == 0 or indent > self.last_indent):
1129
+ quotematch = triple_quotes.match(line)
1130
+ # It would be nice to only run the name_regex when
1131
+ # quotematch wasn't None, but then we mishandle classes
1132
+ # that don't have a docstring.
1133
+ if not self.code_wrapping and self.last_indent >= 0 and indent > self.last_indent:
1134
+ name = name_regex.match(self.last_line)
1135
+ if name:
1136
+ name = name.groups()[0]
1137
+ self.qualified_name[indent] = name
1138
+ elif quotematch:
1139
+ self.qualified_name[indent] = '?'
1140
+ self._update_quotetype(line)
1141
+ if line[indent] != '#' and not self.code_wrapping:
1142
+ self.last_line, self.last_indent = line, indent
1143
+ self.code_wrapping = not (self.paren_count == self.bracket_count == self.curly_count == 0)
1144
+ return quotematch
1145
+
1146
+ def ending_docstring(self, line):
1147
+ r"""
1148
+ Determines whether the input line ends a docstring.
1149
+
1150
+ INPUT:
1151
+
1152
+ - ``line`` -- string, one line of an input file
1153
+
1154
+ OUTPUT: an object that, when evaluated in a boolean context, gives
1155
+ ``True`` or ``False`` depending on whether the input line marks the
1156
+ end of a docstring
1157
+
1158
+ EXAMPLES::
1159
+
1160
+ sage: from sage.doctest.control import DocTestDefaults
1161
+ sage: from sage.doctest.sources import FileDocTestSource
1162
+ sage: from sage.doctest.util import NestedName
1163
+ sage: filename = sage.doctest.sources.__file__
1164
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1165
+ sage: FDS._init()
1166
+ sage: FDS.quotetype = "'''"
1167
+ sage: FDS.ending_docstring("'''")
1168
+ <...Match object...>
1169
+ sage: FDS.ending_docstring('\"\"\"')
1170
+ """
1171
+ quotematch = triple_quotes.match(line)
1172
+ if quotematch is not None and quotematch.groups()[0] != self.quotetype:
1173
+ quotematch = None
1174
+ self._update_quotetype(line)
1175
+ return quotematch
1176
+
1177
+ def _neutralize_doctests(self, reindent):
1178
+ r"""
1179
+ Return a string containing the source of ``self``, but with
1180
+ doctests modified so they are not tested.
1181
+
1182
+ This function is used in creating doctests for ReST files,
1183
+ since docstrings of Python functions defined inside verbatim
1184
+ blocks screw up Python's doctest parsing.
1185
+
1186
+ INPUT:
1187
+
1188
+ - ``reindent`` -- integer; the number of spaces to indent
1189
+ the result
1190
+
1191
+ EXAMPLES::
1192
+
1193
+ sage: from sage.doctest.control import DocTestDefaults
1194
+ sage: from sage.doctest.sources import StringDocTestSource, PythonSource
1195
+ sage: from sage.structure.dynamic_class import dynamic_class
1196
+ sage: s = "'''\n sage: 2 + 2\n 4\n'''"
1197
+ sage: PythonStringSource = dynamic_class('PythonStringSource',
1198
+ ....: (StringDocTestSource, PythonSource))
1199
+ sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime')
1200
+ sage: print(PSS._neutralize_doctests(0))
1201
+ '''
1202
+ safe: 2 + 2
1203
+ 4
1204
+ '''
1205
+ """
1206
+ neutralized = []
1207
+ in_docstring = False
1208
+ self._init()
1209
+ for lineno, line in self:
1210
+ if not line.strip():
1211
+ neutralized.append(line)
1212
+ elif in_docstring:
1213
+ if self.ending_docstring(line):
1214
+ in_docstring = False
1215
+ neutralized.append(" "*reindent + find_prompt.sub(r"\1safe:\3",line))
1216
+ else:
1217
+ if self.starting_docstring(line):
1218
+ in_docstring = True
1219
+ neutralized.append(" "*reindent + line)
1220
+ return "".join(neutralized)
1221
+
1222
+
1223
+ class TexSource(SourceLanguage):
1224
+ """
1225
+ This class defines the functions needed for the extraction of
1226
+ doctests from a LaTeX source.
1227
+
1228
+ EXAMPLES::
1229
+
1230
+ sage: from sage.doctest.control import DocTestDefaults
1231
+ sage: from sage.doctest.sources import FileDocTestSource
1232
+ sage: filename = "sage_paper.tex"
1233
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1234
+ sage: type(FDS)
1235
+ <class 'sage.doctest.sources.TexFileSource'>
1236
+ """
1237
+ # The same line can't both start and end a docstring
1238
+ start_finish_can_overlap = False
1239
+
1240
+ def _init(self):
1241
+ """
1242
+ This function is called before creating doctests from a Tex file.
1243
+
1244
+ EXAMPLES::
1245
+
1246
+ sage: from sage.doctest.control import DocTestDefaults
1247
+ sage: from sage.doctest.sources import FileDocTestSource
1248
+ sage: filename = "sage_paper.tex"
1249
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1250
+ sage: FDS._init()
1251
+ sage: FDS.skipping
1252
+ False
1253
+ """
1254
+ self.skipping = False
1255
+
1256
+ def starting_docstring(self, line):
1257
+ r"""
1258
+ Determines whether the input line starts a docstring.
1259
+
1260
+ Docstring blocks in tex files are defined by verbatim or
1261
+ lstlisting environments, and can be linked together by adding
1262
+ %link immediately after the \end{verbatim} or \end{lstlisting}.
1263
+
1264
+ Within a verbatim (or lstlisting) block, you can tell Sage not to
1265
+ process the rest of the block by including a %skip line.
1266
+
1267
+ INPUT:
1268
+
1269
+ - ``line`` -- string, one line of an input file
1270
+
1271
+ OUTPUT: boolean; whether the input line marks the start of a docstring
1272
+ (verbatim block)
1273
+
1274
+ EXAMPLES::
1275
+
1276
+ sage: from sage.doctest.control import DocTestDefaults
1277
+ sage: from sage.doctest.sources import FileDocTestSource
1278
+ sage: filename = "sage_paper.tex"
1279
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1280
+ sage: FDS._init()
1281
+
1282
+ We start docstrings with \begin{verbatim} or \begin{lstlisting}::
1283
+
1284
+ sage: FDS.starting_docstring(r"\begin{verbatim}")
1285
+ True
1286
+ sage: FDS.starting_docstring(r"\begin{lstlisting}")
1287
+ True
1288
+ sage: FDS.skipping
1289
+ False
1290
+ sage: FDS.ending_docstring("sage: 2+2")
1291
+ False
1292
+ sage: FDS.ending_docstring("4")
1293
+ False
1294
+
1295
+ To start ignoring the rest of the verbatim block, use %skip::
1296
+
1297
+ sage: FDS.ending_docstring("%skip")
1298
+ True
1299
+ sage: FDS.skipping
1300
+ True
1301
+ sage: FDS.starting_docstring("sage: raise RuntimeError")
1302
+ False
1303
+
1304
+ You can even pretend to start another verbatim block while skipping::
1305
+
1306
+ sage: FDS.starting_docstring(r"\begin{verbatim}")
1307
+ False
1308
+ sage: FDS.skipping
1309
+ True
1310
+
1311
+ To stop skipping end the verbatim block::
1312
+
1313
+ sage: FDS.starting_docstring(r"\end{verbatim} %link")
1314
+ False
1315
+ sage: FDS.skipping
1316
+ False
1317
+
1318
+ Linking works even when the block was ended while skipping::
1319
+
1320
+ sage: FDS.linking
1321
+ True
1322
+ sage: FDS.starting_docstring(r"\begin{verbatim}")
1323
+ True
1324
+ """
1325
+ if self.skipping:
1326
+ if self.ending_docstring(line, check_skip=False):
1327
+ self.skipping = False
1328
+ return False
1329
+ return bool(begin_verb.match(line) or begin_lstli.match(line))
1330
+
1331
+ def ending_docstring(self, line, check_skip=True):
1332
+ r"""
1333
+ Determines whether the input line ends a docstring.
1334
+
1335
+ Docstring blocks in tex files are defined by verbatim or
1336
+ lstlisting environments, and can be linked together by adding
1337
+ %link immediately after the \end{verbatim} or \end{lstlisting}.
1338
+
1339
+ Within a verbatim (or lstlisting) block, you can tell Sage not to
1340
+ process the rest of the block by including a %skip line.
1341
+
1342
+ INPUT:
1343
+
1344
+ - ``line`` -- string, one line of an input file
1345
+
1346
+ - ``check_skip`` -- boolean (default: ``True``); used internally in
1347
+ ``starting_docstring``
1348
+
1349
+ OUTPUT: boolean; whether the input line marks the end of a docstring
1350
+ (verbatim block)
1351
+
1352
+ EXAMPLES::
1353
+
1354
+ sage: from sage.doctest.control import DocTestDefaults
1355
+ sage: from sage.doctest.sources import FileDocTestSource
1356
+ sage: filename = "sage_paper.tex"
1357
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1358
+ sage: FDS._init()
1359
+ sage: FDS.ending_docstring(r"\end{verbatim}")
1360
+ True
1361
+ sage: FDS.ending_docstring(r"\end{lstlisting}")
1362
+ True
1363
+ sage: FDS.linking
1364
+ False
1365
+
1366
+ Use %link to link with the next verbatim block::
1367
+
1368
+ sage: FDS.ending_docstring(r"\end{verbatim}%link")
1369
+ True
1370
+ sage: FDS.linking
1371
+ True
1372
+
1373
+ %skip also ends a docstring block::
1374
+
1375
+ sage: FDS.ending_docstring("%skip")
1376
+ True
1377
+ """
1378
+ m = end_verb.match(line)
1379
+ if m:
1380
+ if m.groups()[0]:
1381
+ self.linking = True
1382
+ else:
1383
+ self.linking = False
1384
+ return True
1385
+ m = end_lstli.match(line)
1386
+ if m:
1387
+ if m.groups()[0]:
1388
+ self.linking = True
1389
+ else:
1390
+ self.linking = False
1391
+ return True
1392
+ if check_skip and skip.match(line):
1393
+ self.skipping = True
1394
+ return True
1395
+ return False
1396
+
1397
+
1398
+ class RestSource(SourceLanguage):
1399
+ """
1400
+ This class defines the functions needed for the extraction of
1401
+ doctests from ReST sources.
1402
+
1403
+ EXAMPLES::
1404
+
1405
+ sage: from sage.doctest.control import DocTestDefaults
1406
+ sage: from sage.doctest.sources import FileDocTestSource
1407
+ sage: filename = "sage_doc.rst"
1408
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1409
+ sage: type(FDS)
1410
+ <class 'sage.doctest.sources.RestFileSource'>
1411
+ """
1412
+ # The same line can both start and end a docstring
1413
+ start_finish_can_overlap = True
1414
+
1415
+ def _init(self):
1416
+ """
1417
+ This function is called before creating doctests from a ReST file.
1418
+
1419
+ EXAMPLES::
1420
+
1421
+ sage: from sage.doctest.control import DocTestDefaults
1422
+ sage: from sage.doctest.sources import FileDocTestSource
1423
+ sage: filename = "sage_doc.rst"
1424
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1425
+ sage: FDS._init()
1426
+ sage: FDS.link_all
1427
+ False
1428
+ """
1429
+ self.link_all = False
1430
+ self.last_line = ""
1431
+ self.last_indent = -1
1432
+ self.first_line = False
1433
+ self.skipping = False
1434
+
1435
+ def starting_docstring(self, line):
1436
+ """
1437
+ A line ending with a double colon starts a verbatim block in a ReST file,
1438
+ as does a line containing ``.. CODE-BLOCK:: language``.
1439
+
1440
+ This function also determines whether the docstring block
1441
+ should be joined with the previous one, or should be skipped.
1442
+
1443
+ INPUT:
1444
+
1445
+ - ``line`` -- string; one line of an input file
1446
+
1447
+ OUTPUT: either ``None`` or a Match object
1448
+
1449
+ EXAMPLES::
1450
+
1451
+ sage: from sage.doctest.control import DocTestDefaults
1452
+ sage: from sage.doctest.sources import FileDocTestSource
1453
+ sage: filename = "sage_doc.rst"
1454
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1455
+ sage: FDS._init()
1456
+ sage: FDS.starting_docstring("Hello world::")
1457
+ True
1458
+ sage: FDS.ending_docstring(" sage: 2 + 2")
1459
+ False
1460
+ sage: FDS.ending_docstring(" 4")
1461
+ False
1462
+ sage: FDS.ending_docstring("We are now done")
1463
+ True
1464
+ sage: FDS.starting_docstring(".. link")
1465
+ sage: FDS.starting_docstring("::")
1466
+ True
1467
+ sage: FDS.linking
1468
+ True
1469
+ """
1470
+ if link_all.match(line):
1471
+ self.link_all = True
1472
+ if self.skipping:
1473
+ end_block = self.ending_docstring(line)
1474
+ if end_block:
1475
+ self.skipping = False
1476
+ else:
1477
+ return False
1478
+ m1 = double_colon.match(line)
1479
+ m2 = code_block.match(line.lower())
1480
+ starting = (m1 and not line.strip().startswith(".. ")) or m2
1481
+ if starting:
1482
+ self.linking = self.link_all or '.. link' in self.last_line
1483
+ self.first_line = True
1484
+ m = m1 or m2
1485
+ indent = len(m.groups()[0])
1486
+ if '.. skip' in self.last_line:
1487
+ self.skipping = True
1488
+ starting = False
1489
+ else:
1490
+ indent = self.last_indent
1491
+ self.last_line, self.last_indent = line, indent
1492
+ return starting
1493
+
1494
+ def ending_docstring(self, line):
1495
+ """
1496
+ When the indentation level drops below the initial level the
1497
+ block ends.
1498
+
1499
+ INPUT:
1500
+
1501
+ - ``line`` -- string; one line of an input file
1502
+
1503
+ OUTPUT: boolean; whether the verbatim block is ending
1504
+
1505
+ EXAMPLES::
1506
+
1507
+ sage: from sage.doctest.control import DocTestDefaults
1508
+ sage: from sage.doctest.sources import FileDocTestSource
1509
+ sage: filename = "sage_doc.rst"
1510
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1511
+ sage: FDS._init()
1512
+ sage: FDS.starting_docstring("Hello world::")
1513
+ True
1514
+ sage: FDS.ending_docstring(" sage: 2 + 2")
1515
+ False
1516
+ sage: FDS.ending_docstring(" 4")
1517
+ False
1518
+ sage: FDS.ending_docstring("We are now done")
1519
+ True
1520
+ """
1521
+ if not line.strip():
1522
+ return False
1523
+ indent = whitespace.match(line).end()
1524
+ if self.first_line:
1525
+ self.first_line = False
1526
+ if indent <= self.last_indent:
1527
+ # We didn't indent at all
1528
+ return True
1529
+ self.last_indent = indent
1530
+ return indent < self.last_indent
1531
+
1532
+ def parse_docstring(self, docstring, namespace, start):
1533
+ r"""
1534
+ Return a list of doctest defined in this docstring.
1535
+
1536
+ Code blocks in a REST file can contain python functions with
1537
+ their own docstrings in addition to in-line doctests. We want
1538
+ to include the tests from these inner docstrings, but Python's
1539
+ doctesting module has a problem if we just pass on the whole
1540
+ block, since it expects to get just a docstring, not the
1541
+ Python code as well.
1542
+
1543
+ Our solution is to create a new doctest source from this code
1544
+ block and append the doctests created from that source. We
1545
+ then replace the occurrences of "sage:" and ">>>" occurring
1546
+ inside a triple quote with "safe:" so that the doctest module
1547
+ doesn't treat them as tests.
1548
+
1549
+ EXAMPLES::
1550
+
1551
+ sage: from sage.doctest.control import DocTestDefaults
1552
+ sage: from sage.doctest.sources import FileDocTestSource
1553
+ sage: from sage.doctest.parsing import SageDocTestParser
1554
+ sage: from sage.doctest.util import NestedName
1555
+ sage: filename = "sage_doc.rst"
1556
+ sage: FDS = FileDocTestSource(filename, DocTestDefaults())
1557
+ sage: FDS.parser = SageDocTestParser(set(['sage']))
1558
+ sage: FDS.qualified_name = NestedName('sage_doc')
1559
+ sage: s = "Some text::\n\n def example_python_function(a, \
1560
+ ....: b):\n '''\n Brief description \
1561
+ ....: of function.\n\n EXAMPLES::\n\n \
1562
+ ....: sage: test1()\n sage: test2()\n \
1563
+ ....: '''\n return a + b\n\n sage: test3()\n\nMore \
1564
+ ....: ReST documentation."
1565
+ sage: tests = FDS.parse_docstring(s, {}, 100)
1566
+ sage: len(tests)
1567
+ 2
1568
+ sage: for ex in tests[0].examples:
1569
+ ....: print(ex.sage_source)
1570
+ test3()
1571
+ sage: for ex in tests[1].examples:
1572
+ ....: print(ex.sage_source)
1573
+ test1()
1574
+ test2()
1575
+ sig_on_count() # check sig_on/off pairings (virtual doctest)
1576
+ """
1577
+ PythonStringSource = dynamic_class("sage.doctest.sources.PythonStringSource",
1578
+ (StringDocTestSource, PythonSource))
1579
+ min_indent = self.parser._min_indent(docstring)
1580
+ pysource = '\n'.join(l[min_indent:] for l in docstring.split('\n'))
1581
+ inner_source = PythonStringSource(self.basename, pysource,
1582
+ self.options,
1583
+ self.printpath,
1584
+ lineno_shift=start + 1)
1585
+ inner_doctests, _ = inner_source._create_doctests(namespace, True)
1586
+ safe_docstring = inner_source._neutralize_doctests(min_indent)
1587
+ outer_doctest = self.parser.get_doctest(safe_docstring, namespace,
1588
+ str(self.qualified_name),
1589
+ self.printpath, start + 1)
1590
+ return [outer_doctest] + inner_doctests
1591
+
1592
+
1593
+ class DictAsObject(dict):
1594
+ """
1595
+ A simple subclass of dict that inserts the items from the initializing dictionary into attributes.
1596
+
1597
+ EXAMPLES::
1598
+
1599
+ sage: from sage.doctest.sources import DictAsObject
1600
+ sage: D = DictAsObject({'a':2})
1601
+ sage: D.a
1602
+ 2
1603
+ """
1604
+ def __init__(self, attrs):
1605
+ """
1606
+ Initialization.
1607
+
1608
+ INPUT:
1609
+
1610
+ - ``attrs`` -- dictionary
1611
+
1612
+ EXAMPLES::
1613
+
1614
+ sage: from sage.doctest.sources import DictAsObject
1615
+ sage: D = DictAsObject({'a':2})
1616
+ sage: D.a == D['a']
1617
+ True
1618
+ sage: D.a
1619
+ 2
1620
+ """
1621
+ super().__init__(attrs)
1622
+ self.__dict__.update(attrs)
1623
+
1624
+ def __setitem__(self, ky, val):
1625
+ """
1626
+ We preserve the ability to access entries through either the
1627
+ dictionary or attribute interfaces.
1628
+
1629
+ EXAMPLES::
1630
+
1631
+ sage: from sage.doctest.sources import DictAsObject
1632
+ sage: D = DictAsObject({})
1633
+ sage: D['a'] = 2
1634
+ sage: D.a
1635
+ 2
1636
+ """
1637
+ super().__setitem__(ky, val)
1638
+ try:
1639
+ super().__setattr__(ky, val)
1640
+ except TypeError:
1641
+ pass
1642
+
1643
+ def __setattr__(self, ky, val):
1644
+ """
1645
+ We preserve the ability to access entries through either the
1646
+ dictionary or attribute interfaces.
1647
+
1648
+ EXAMPLES::
1649
+
1650
+ sage: from sage.doctest.sources import DictAsObject
1651
+ sage: D = DictAsObject({})
1652
+ sage: D.a = 2
1653
+ sage: D['a']
1654
+ 2
1655
+ """
1656
+ super().__setitem__(ky, val)
1657
+ super().__setattr__(ky, val)