r"""
Format Sage documentation for viewing with IPython and the notebook
AUTHORS:
- William Stein (2005): initial version.
- Nick Alexander (2007): nodetex functions
- Nick Alexander (2008): search_src, search_def improvements
- Martin Albrecht (2008-03-21): parse LaTeX description environments in sagedoc
- John Palmieri (2009-04-11): fix for #5754 plus doctests
- Dan Drake (2009-05-21): refactor search_* functions, use system 'find' instead of sage -grep
- John Palmieri (2009-06-28): don't use 'find' -- use Python (os.walk, re.search) instead.
- Simon King (2011-09): Use os.linesep, avoid destruction of embedding information,
enable nodetex in a docstring. Consequently use sage_getdoc.
- Marc Culler (2023--): modifications for Sage_macOS
TESTS:
Check that argspecs of extension function/methods appear correctly,
see :issue:`12849`::
sage: from sage.env import SAGE_DOC
sage: docfilename = os.path.join(SAGE_DOC, 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html')
sage: with open(docfilename) as fobj: # needs sagemath_doc_html
....: for line in fobj:
....: if "#sage.symbolic.expression.Expression.numerical_approx" in line:
....: print(line)
<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>...
Check that sphinx is not imported at Sage start-up::
sage: os.system("sage -c \"if 'sphinx' in sys.modules: sys.exit(1)\"")
0
"""
import os
import re
import shutil
import sys
import pydoc
from sage.misc.temporary_file import tmp_dir
from sage.misc.viewer import browser
from sage.misc import sageinspect
import sage.version
from sage.env import SAGE_DOC, SAGE_SRC
app_contents = os.path.abspath(os.path.join(os.environ['SAGE_ROOT'],
'..', '..', '..', '..'))
site_root = os.path.join(app_contents, 'Resources', 'documentation')
try:
from cocoserver import StaticServer
doc_server = StaticServer(site_root)
except ImportError:
doc_server = None
math_substitutes = [
(r'\\to', '-->'),
(r'\\rightarrow', '-->'),
(r'\\leftarrow', '<--'),
(r'\\leftrightarrow', '<->'),
(r'\\longrightarrow', '--->'),
(r'\\longleftarrow', '<---'),
(r'\\longleftrightarrow', '<-->'),
(r'\\Rightarrow', '==>'),
(r'\\Leftarrow', '<=='),
(r'\\Leftrightarrow', '<=>'),
(r'\\Longrightarrow', '===>'),
(r'\\Longleftarrow', '<==='),
(r'\\Longleftrightarrow', '<==>'),
(r'\\colon', ':'),
(r'\\left', ''),
(r'\\right', ''),
(r'\\bigl', ''),
(r'\\bigr', ''),
(r'\\leq', '<='),
(r'\\geq', '>='),
(r'\\le', '<='),
(r'\\ge', '>='),
(r'\\cdots', '...'),
(r'\\ldots', '...'),
(r'\\dots', '...'),
(r'\\cdot', ' *'),
(r'\\ast', ' *'),
(r' \\times', ' x'),
(r'\\times', ' x'),
(r'\\backslash', '\\'),
(r'\\mapsto', ' |--> '),
(r'\\longmapsto', ' |---> '),
(r'\\lvert', '|'),
(r'\\rvert', '|'),
(r'\\mid', '|'),
(r' \\circ', ' o'),
(r'\\circ', ' o')
]
nonmath_substitutes = [
('\\_', '_'),
('\\item', '* '),
('<BLANKLINE>', ''),
('\\bf', ''),
('\\sage', 'Sage'),
('\\SAGE', 'Sage'),
('\\Sage', 'Sage'),
('\\rm', ''),
('backslash', '\\'),
('begin{enumerate}', ''),
('end{enumerate}', ''),
('begin{description}', ''),
('end{description}', ''),
('begin{itemize}', ''),
('end{itemize}', ''),
('begin{verbatim}', ''),
('end{verbatim}', ''),
('note{', 'NOTE: '),
]
app_contents = os.path.abspath(os.path.join(os.environ['SAGE_ROOT'],
'..', '..', '..', '..'))
site_root = os.path.join(app_contents, 'Resources', 'documentation')
try:
from cocoserver import StaticServer
doc_server = StaticServer(site_root)
except ImportError:
doc_server = None
def _rmcmd(s, cmd, left='', right=''):
"""
Remove the LaTeX command ``cmd`` from the string ``s``. This
function is used by ``detex``.
INPUT:
- ``s`` -- (string) string from which to remove the command
- ``cmd`` -- (string) command to be removed. This should be a
command which takes a single argument, like 'emph' or 'url'; the
command is removed, but its argument is not.
- ``left``, ``right`` -- (string, default: '') add these
strings at the left and right ends of the command. See the
examples.
EXAMPLES::
sage: from sage.misc.sagedoc import _rmcmd
sage: _rmcmd('Check out \\url{http://www.sagemath.org}.', 'url')
'Check out http://www.sagemath.org.'
sage: _rmcmd('Text in \\emph{italics} looks like this.', 'emph', '*', '*')
'Text in *italics* looks like this.'
sage: _rmcmd('This is a \\very{silly} example.', 'very', right='!?')
'This is a silly!? example.'
"""
c = '\\%s{' % cmd
while True:
i = s.find(c)
if i == -1:
return s
nesting = 1
j = i + len(c) + 1
while j < len(s) and nesting > 0:
if s[j] == '{':
nesting += 1
elif s[j] == '}':
nesting -= 1
j += 1
j -= 1
if j < len(s):
s = s[:i] + left + s[i + len(c):j] + right + s[j + 1:]
else:
return s
itempattern = re.compile(r"\\item\[?([^]]*)\]? *(.*)")
itemreplace = r"* \1 \2"
def detex(s, embedded=False):
r"""nodetex
This strips LaTeX commands from a string; it is used by the
``format`` function to process docstrings for display from the
command line interface.
INPUT:
- ``s`` -- string
- ``embedded`` -- boolean (default: ``False``)
If ``embedded`` is False, then do the replacements in both
``math_substitutes`` and ``nonmath_substitutes``. If True, then
only do ``nonmath_substitutes``.
OUTPUT:
string
EXAMPLES::
sage: from sage.misc.sagedoc import detex
sage: detex(r'Some math: `n \geq k`. A website: \url{sagemath.org}.')
'Some math: n >= k. A website: sagemath.org.\n'
sage: detex(r'More math: `x \mapsto y`. {\bf Bold face}.')
'More math: x |--> y. { Bold face}.\n'
sage: detex(r'`a, b, c, \ldots, z`')
'a, b, c, ..., z\n'
sage: detex(r'`a, b, c, \ldots, z`', embedded=True)
'`a, b, c, \\ldots, z`'
sage: detex(r'`\left(\lvert x\ast y \rvert\right]`')
'(| x * y |]\n'
sage: detex(r'`\left(\leq\le\leftarrow \rightarrow\unknownmacro\to`')
'(<=<=<-- -->\\unknownmacro-->\n'
"""
s = _rmcmd(s, 'url')
s = _rmcmd(s, 'code')
s = _rmcmd(s, 'class')
s = _rmcmd(s, 'mbox')
s = _rmcmd(s, 'text')
s = _rmcmd(s, 'section')
s = _rmcmd(s, 'subsection')
s = _rmcmd(s, 'subsubsection')
s = _rmcmd(s, 'note', 'NOTE: ', '')
s = _rmcmd(s, 'emph', '*', '*')
s = _rmcmd(s, 'textbf', '*', '*')
s = re.sub(itempattern, itemreplace, s)
for a, b in nonmath_substitutes:
s = s.replace(a, b)
if not embedded:
s = _rmcmd(s, 'mathop')
s = _rmcmd(s, 'mathrm')
try:
from .sphinxify import sphinxify
except ImportError:
s = s.replace('``', '"').replace('`', '') + '\n'
else:
s = sphinxify(s, format='text')
for a, b in math_substitutes:
s = re.sub(a + '([^a-zA-Z])', b + '\\1', s)
return s
def skip_TESTS_block(docstring):
r"""
Remove blocks labeled "TESTS:" from ``docstring``.
INPUT:
- ``docstring``, a string
A "TESTS" block is a block starting "TESTS:" (or
the same with two colons), on a line on its own, and ending either
with a line indented less than "TESTS", or with a line with the
same level of indentation -- not more -- matching one of the
following:
- a Sphinx directive of the form ".. foo:", optionally followed by
other text.
- text of the form "UPPERCASE:", optionally followed by other
text.
- lines which look like a reST header: one line containing
anything, followed by a line consisting only of a string of
hyphens, equal signs, or other characters which are valid
markers for reST headers: ``- = ` : ' " ~ _ ^ * + # < >``.
However, lines only containing double colons `::` do not
end "TESTS" blocks.
Return the string obtained from ``docstring`` by removing these
blocks.
EXAMPLES::
sage: from sage.misc.sagedoc import skip_TESTS_block
sage: start = ' Docstring\n\n'
sage: test = ' TESTS: \n\n Here is a test::\n sage: 2+2 \n 5 \n\n'
sage: test2 = ' TESTS:: \n\n sage: 2+2 \n 6 \n\n'
Test lines starting with "REFERENCES:"::
sage: refs = ' REFERENCES: \n text text \n'
sage: skip_TESTS_block(start + test + refs).rstrip() == (start + refs).rstrip()
True
sage: skip_TESTS_block(start + test + test2 + refs).rstrip() == (start + refs).rstrip()
True
sage: skip_TESTS_block(start + test + refs + test2).rstrip() == (start + refs).rstrip()
True
Test Sphinx directives::
sage: directive = ' .. todo:: \n do some stuff \n'
sage: skip_TESTS_block(start + test + refs + test2 + directive).rstrip() == (start + refs + directive).rstrip()
True
Test unindented lines::
sage: unindented = 'NOT INDENTED\n'
sage: skip_TESTS_block(start + test + unindented).rstrip() == (start + unindented).rstrip()
True
sage: skip_TESTS_block(start + test + unindented + test2 + unindented).rstrip() == (start + unindented + unindented).rstrip()
True
Test headers::
sage: header = ' Header:\n ~~~~~~~~'
sage: skip_TESTS_block(start + test + header) == start + header
True
Not a header because the characters on the second line must all be
the same::
sage: fake_header = ' Header:\n -=-=-=-=-='
sage: skip_TESTS_block(start + test + fake_header).rstrip() == start.rstrip()
True
Not a header because it's indented compared to 'TEST' in the
string ``test``::
sage: another_fake = '\n blah\n ----'
sage: skip_TESTS_block(start + test + another_fake).rstrip() == start.rstrip()
True
Double colons ``::`` are also not considered as headers (:issue:`27896`)::
sage: colons = ' ::\n\n sage: 2+2\n 4\n\n'
sage: skip_TESTS_block(start + test2 + colons).rstrip() == start.rstrip()
True
"""
tests_block = re.compile('([ ]*)TEST[S]?:[:]?[ ]*$')
end_of_block = re.compile(r'[ ]*(\.\.[ ]+[-_A-Za-z]+|[A-Z]+):')
header = re.compile(r'^[ ]*(?:([-=`\'"~_^*+#><])\1+|:|:::+)[ ]*$')
s = ''
skip = False
previous = ''
indentation = ''
for l in docstring.split('\n'):
if not skip:
m = tests_block.match(l)
if m:
skip = True
indentation = m.group(1)
else:
s += "\n"
s += l
else:
if l and not l.isspace() and not l.startswith(indentation):
skip = False
s += "\n"
s += l
elif end_of_block.match(l) and not tests_block.match(l):
if l.startswith(indentation + " "):
continue
skip = False
s += "\n"
s += l
elif header.match(l):
if l.startswith(indentation + " "):
continue
skip = False
if previous:
s += "\n"
s += previous
s += "\n"
s += l
previous = l
return s[1:]
def process_dollars(s):
r"""nodetex
Replace dollar signs with backticks.
More precisely, do a regular expression search. Replace a plain
dollar sign ($) by a backtick (`). Replace an escaped dollar sign
(\\$) by a dollar sign ($). Don't change a dollar sign preceded or
followed by a backtick (\`$ or \$`), because of strings like
"``$HOME``". Don't make any changes on lines starting with more
spaces than the first nonempty line in ``s``, because those are
indented and hence part of a block of code or examples.
This also doesn't replaces dollar signs enclosed in curly braces,
to avoid nested math environments.
EXAMPLES::
sage: from sage.misc.sagedoc import process_dollars
sage: process_dollars('hello')
'hello'
sage: process_dollars('some math: $x=y$')
doctest:warning...
DeprecationWarning: using dollar signs to mark up math in Sage docstrings
is deprecated; use backticks instead
See https://github.com/sagemath/sage/issues/33973 for details.
'some math: `x=y`'
Replace \\$ with $, and don't do anything when backticks are involved::
sage: process_dollars(r'a ``$REAL`` dollar sign: \$')
'a ``$REAL`` dollar sign: $'
Don't make any changes on lines indented more than the first
nonempty line::
sage: s = '\n first line\n indented $x=y$'
sage: s == process_dollars(s)
True
Don't replace dollar signs enclosed in curly braces::
sage: process_dollars(r'f(n) = 0 \text{ if $n$ is prime}')
'f(n) = 0 \\text{ if $n$ is prime}'
This is not perfect::
sage: process_dollars(r'$f(n) = 0 \text{ if $n$ is prime}$')
'`f(n) = 0 \\text{ if $n$ is prime}$'
The regular expression search doesn't find the last $.
Fortunately, there don't seem to be any instances of this kind of
expression in the Sage library, as of this writing.
"""
if s.find("$") == -1:
return s
from sage.misc.superseded import deprecation
whitespace = re.match(r'\s*\S', s.lstrip('\n'))
whitespace = ' ' * (whitespace.end() - 1)
indices = [0]
for m in re.finditer(r"{[^{}$]*\$([^{}$]*)\$[^{}$]*}", s):
indices[-1] = (indices[-1], m.start())
indices.append(m.end())
indices[-1] = (indices[-1], len(s))
regexp = "^" + "(%s)?" % whitespace + r"(\S.*?)?(?<!`|\\)\$(?!`)"
dollar = re.compile(regexp, re.M)
slashdollar = re.compile(r"\\\$")
for start, end in indices:
while dollar.search(s, start, end):
m = dollar.search(s, start, end)
s = s[:m.end() - 1] + "`" + s[m.end():]
deprecation(33973,
"using dollar signs to mark up math in Sage docstrings "
"is deprecated; use backticks instead")
while slashdollar.search(s, start, end):
m = slashdollar.search(s, start, end)
s = s[:m.start()] + "$" + s[m.end():]
return s
pythonversion = sys.version.split(' ')[0]
extlinks = {
'python': (f'https://docs.python.org/release/{pythonversion}/%s', None),
'issue': ('https://github.com/sagemath/sage/issues/%s', 'Issue #%s'),
'sage_root': ('https://github.com/sagemath/sage/tree/develop/%s', 'SAGE_ROOT/%s'),
'wikipedia': ('https://en.wikipedia.org/wiki/%s', 'Wikipedia article %s'),
'arxiv': ('https://arxiv.org/abs/%s', 'arXiv %s'),
'oeis': ('https://oeis.org/%s', 'OEIS sequence %s'),
'doi': ('https://doi.org/%s', 'doi:%s'),
'pari': ('https://pari.math.u-bordeaux.fr/dochtml/help/%s', 'pari:%s'),
'mathscinet': ('https://www.ams.org/mathscinet-getitem?mr=%s', 'MathSciNet %s'),
'common_lisp': ('https://www.lispworks.com/documentation/lw50/CLHS/Body/%s.htm', 'Common Lisp: %s'),
'ecl': ('https://ecl.common-lisp.dev/static/manual/%s.html', 'ECL: %s'),
'gap': ('https://docs.gap-system.org/doc/ref/%s_mj.html', 'GAP: %s'),
'gap_package': ('https://docs.gap-system.org/pkg/%s', 'GAP package %s'),
'giac_cascmd': ('https://www-fourier.ujf-grenoble.fr/~parisse/giac/doc/en/cascmd_en/%s.html', 'Giac: %s'),
'giac_us': ('https://www-fourier.ujf-grenoble.fr/~parisse/giac_us.html#%s', 'Giac API: %s'),
'maxima': ('https://maxima.sourceforge.io/docs/manual/maxima_singlepage.html#%s', 'Maxima: %s'),
'meson': ('https://mesonbuild.com/%s', 'Meson: %s'),
'polymake': ('https://polymake.org/doku.php/documentation/latest/%s', 'polymake: %s'),
'ppl': ('https://www.bugseng.com/products/ppl/documentation/user/ppl-user-1.2-html/%s.html', 'PPL: %s'),
'qepcad': ('https://www.usna.edu/CS/qepcadweb/B/%s.html', 'QEPCAD: %s'),
'scip': ('https://scipopt.org/doc/html/%s.php', 'SCIP: %s'),
'singular': ('https://www.singular.uni-kl.de/Manual/4-3-2/%s.htm', 'Singular: %s'),
'soplex': ('https://soplex.zib.de/doc/html/%s.php', 'SoPlex: %s'),
}
def process_extlinks(s, embedded=False):
r"""nodetex
In docstrings at the command line, process markup related to the
Sphinx extlinks extension. For example, replace ``:issue:`NUM```
with ``https://github.com/sagemath/sage/issues/NUM``, and similarly with
``:python:TEXT`` and ``:wikipedia:TEXT``, looking up the url from
the dictionary ``extlinks`` in ``sage_docbuild.conf``.
If ``TEXT`` is of the form ``blah <LINK>``, then it uses ``LINK``
rather than ``TEXT`` to construct the url.
In the notebook, don't do anything: let sphinxify take care of it.
INPUT:
- ``s`` -- string, in practice a docstring
- ``embedded`` -- boolean (default: ``False``)
This function is called by :func:`format`, and if in the notebook,
it sets ``embedded`` to be ``True``, otherwise ``False``.
EXAMPLES::
sage: from sage.misc.sagedoc import process_extlinks
sage: process_extlinks('See :issue:`1234`, :wikipedia:`Wikipedia <Sage_(mathematics_software)>`, and :issue:`4321` ...')
'See https://github.com/sagemath/sage/issues/1234, https://en.wikipedia.org/wiki/Sage_(mathematics_software), and https://github.com/sagemath/sage/issues/4321 ...'
sage: process_extlinks('See :issue:`1234` for more information.', embedded=True)
'See :issue:`1234` for more information.'
sage: process_extlinks('see :python:`Implementing Descriptors <reference/datamodel.html#implementing-descriptors>` ...')
'see https://docs.python.org/release/.../reference/datamodel.html#implementing-descriptors ...'
"""
if embedded:
return s
for key in extlinks:
while True:
m = re.search(':%s:`([^`]*)`' % key, s)
if not m:
break
link = m.group(1)
m = re.search('.*<([^>]*)>', link)
if m:
link = m.group(1)
s = re.sub(':%s:`([^`]*)`' % key,
extlinks[key][0].replace('%s', link),
s, count=1)
return s
def process_mathtt(s):
r"""nodetex
Replace \\mathtt{BLAH} with BLAH in the command line.
INPUT:
- ``s`` -- string, in practice a docstring
This function is called by :func:`format`.
EXAMPLES::
sage: from sage.misc.sagedoc import process_mathtt
sage: process_mathtt(r'e^\mathtt{self}')
'e^self'
"""
while True:
start = s.find("\\mathtt{")
end = s.find("}", start)
if start == -1 or end == -1:
break
s = s[:start] + s[start + 8:end] + s[end + 1:]
return s
def process_optional_doctest_tags(s):
r"""
Remove ``# optional/needs`` doctest tags for present features from docstring ``s``.
EXAMPLES:
sage: from sage.misc.sagedoc import process_optional_doctest_tags
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
"sage: K.<x> = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2"
"""
import io
from sage.doctest.external import available_software
from sage.doctest.parsing import parse_optional_tags, update_optional_tags
start = 0
with io.StringIO() as output:
for m in re.finditer('( *sage: *.*#.*)\n', s):
output.write(s[start:m.start(0)])
line = m.group(1)
tags = [tag for tag in parse_optional_tags(line)
if tag not in available_software]
line = update_optional_tags(line, tags=tags)
if not re.fullmatch(' *sage: *', line):
print(line, file=output)
start = m.end(0)
output.write(s[start:])
return output.getvalue()
def format(s, embedded=False):
r"""noreplace
Format Sage documentation ``s`` for viewing with IPython.
This calls :func:`detex` on ``s`` to convert LaTeX commands to plain
text, unless the directive ``nodetex`` is given in the first line
of the string.
Also, if ``s`` contains a string of the form ``<<<obj>>>``, then
it replaces it with the docstring for ``obj``, unless the
directive ``noreplace`` is given in the first line. If an error
occurs under the attempt to find the docstring for ``obj``, then
the substring ``<<<obj>>>`` is preserved.
Directives must be separated by a comma.
INPUT:
- ``s`` -- string
- ``embedded`` -- boolean (default: ``False``)
OUTPUT: string
Set ``embedded`` equal to ``True`` if formatting for use in the
notebook; this just gets passed as an argument to :func:`detex`.
.. SEEALSO::
:func:`sage.misc.sageinspect.sage_getdoc` to get the formatted
documentation of a given object.
EXAMPLES::
sage: from sage.misc.sagedoc import format
sage: identity_matrix(2).rook_vector.__doc__[191:263] # needs sage.modules
'Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard'
sage: format(identity_matrix(2).rook_vector.__doc__[191:263]) # needs sage.modules
'Let A be an m by n (0,1)-matrix. We identify A with a chessboard\n'
If the first line of the string is 'nodetex', remove 'nodetex' but
don't modify any TeX commands::
sage: format("nodetex\n`x \\geq y`")
'`x \\geq y`'
Testing a string enclosed in triple angle brackets::
sage: format('<<<identity_matrix')
'<<<identity_matrix\n'
sage: format('identity_matrix>>>')
'identity_matrix>>>\n'
sage: format('<<<identity_matrix>>>') # needs sage.modules
'...Definition: identity_matrix(...'
sage: format('<<<identity_matrix>>>')[:28] # needs sphinx
'Definition: identity_matrix('
TESTS:
We check that the todo Sphinx extension is correctly activated::
sage: sage.misc.sagedoc.format(sage.combinat.ranker.on_fly.__doc__) # needs sphinx
" Returns ... Todo: add tests as in combinat::rankers\n"
In the following use case, the ``nodetex`` directive would have been ignored prior
to :issue:`11815`::
sage: cython_code = ["def testfunc(x):",
....: " '''",
....: " nodetex",
....: " This is a doc string with raw latex",
....: "",
....: " `x \\geq y`",
....: " '''",
....: " return -x"]
sage: cython('\n'.join(cython_code)) # needs sage.misc.cython
sage: from sage.misc.sageinspect import sage_getdoc
sage: print(sage_getdoc(testfunc)) # needs sage.misc.cython
<BLANKLINE>
This is a doc string with raw latex
<BLANKLINE>
`x \geq y`
<BLANKLINE>
We check that the ``noreplace`` directive works, even combined with
``nodetex`` (see :issue:`11817`)::
sage: print(format('''nodetex, noreplace\n<<<identity_matrix>>>`\\not= 0`'''))
<<<identity_matrix>>>`\not= 0`
If replacement is impossible, then no error is raised::
sage: print(format('<<<bla\n<<<bla>>>\n<<<identity_matrix>>>'))
<<<bla <<<bla>>>
<BLANKLINE>
Definition: identity_matrix(ring, n=0, sparse=False)
<BLANKLINE>
This function is available as identity_matrix(...) and
matrix.identity(...).
<BLANKLINE>
Return the n x n identity matrix over the given ring.
...
Check that backslashes are preserved in code blocks (:issue:`29140`)::
sage: format('::\n' # needs sphinx
....: '\n'
....: r' sage: print(r"\\\\.")' '\n'
....: r' \\\\.')
' sage: print(r"\\\\\\\\.")\n \\\\\\\\.\n'
sage: format(r'inline code ``\\\\.``')
'inline code "\\\\\\\\."\n'
"""
if not isinstance(s, str):
raise TypeError("s must be a string")
s = s.lstrip(os.linesep)
first_newline = s.find(os.linesep)
if first_newline > -1:
first_line = s[:first_newline]
else:
first_line = s
directives = [d.strip().lower() for d in first_line.split(',')]
if 'noreplace' in directives or 'nodetex' in directives:
s = s[first_newline + len(os.linesep):]
try:
import sage.all
except ImportError:
pass
docs = set()
if 'noreplace' not in directives:
i_0 = 0
while True:
i = s[i_0:].find("<<<")
if i == -1:
break
j = s[i_0 + i + 3:].find('>>>')
if j == -1:
break
obj = s[i_0 + i + 3:i_0 + i + 3 + j]
if obj in docs:
t = ''
else:
try:
x = eval('sage.all.%s' % obj, locals())
except AttributeError:
i_0 += i + 6 + j
continue
except SyntaxError:
i_0 += i + 3
continue
t0 = sage.misc.sageinspect.sage_getdef(x, obj)
t1 = sage.misc.sageinspect.sage_getdoc(x)
t = 'Definition: ' + t0 + '\n\n' + t1
docs.add(obj)
s = s[:i_0 + i] + '\n' + t + s[i_0 + i + 6 + j:]
i_0 += i
if 'nodetex' not in directives:
s = process_dollars(s)
s = skip_TESTS_block(s)
if not embedded:
s = process_mathtt(s)
s = process_extlinks(s, embedded=embedded)
s = detex(s, embedded=embedded)
if not embedded:
s = process_optional_doctest_tags(s)
return s
def format_src(s):
"""
Format Sage source code ``s`` for viewing with IPython.
If ``s`` contains a string of the form "<<<obj>>>", then it
replaces it with the source code for "obj".
INPUT:
- ``s`` -- string
OUTPUT: string
EXAMPLES::
sage: from sage.misc.sagedoc import format_src
sage: format_src('unladen swallow')
'unladen swallow'
sage: format_src('<<<Sq>>>')[5:15] # needs sage.combinat sage.modules
'Sq(*nums):'
"""
if not isinstance(s, str):
raise TypeError("s must be a string")
docs = set()
try:
import sage.all
except ImportError:
pass
while True:
i = s.find("<<<")
if i == -1:
break
j = s[i + 3:].find('>>>')
if j == -1:
break
obj = s[i + 3:i + 3 + j]
if obj in docs:
t = ''
else:
x = eval('sage.all.%s' % obj, locals())
t = my_getsource(x)
docs.add(obj)
if t is None:
print(x)
t = ''
s = s[:i] + '\n' + t + s[i + 6 + j:]
return s
def _search_src_or_doc(what, string, extra1='', extra2='', extra3='',
extra4='', extra5='', **kwargs):
r"""
Search the Sage library or documentation for lines containing
``string`` and possibly some other terms. This function is used by
:func:`search_src`, :func:`search_doc`, and :func:`search_def`.
INPUT:
- ``what``: either ``'src'`` or ``'doc'``, according to whether you
are searching the documentation or source code.
- the rest of the input is the same as :func:`search_src`,
:func:`search_doc`, and :func:`search_def`.
OUTPUT:
If ``interact`` is ``False``, a string containing the results;
otherwise, there is no output and the results are presented
according to whether you are using the notebook or command-line
interface. In the command-line interface, each line of the results
has the form ``filename:num:line of code``, where ``num`` is the
line number in ``filename`` and ``line of code`` is the line that
matched your search terms.
EXAMPLES::
sage: from sage.misc.sagedoc import _search_src_or_doc
sage: print(_search_src_or_doc('src', r'matrix\(', # long time random
....: 'incidence_structures', 'self',
....: '^combinat', interact=False))
misc/sagedoc.py: sage: _search_src_or_doc('src', 'matrix(', 'incidence_structures', 'self', '^combinat', interact=False)
combinat/designs/incidence_structures.py: M1 = self.incidence_matrix()
combinat/designs/incidence_structures.py: A = self.incidence_matrix()
combinat/designs/incidence_structures.py: M = transpose(self.incidence_matrix())
combinat/designs/incidence_structures.py: def incidence_matrix(self):
combinat/designs/incidence_structures.py: A = self.incidence_matrix()
combinat/designs/incidence_structures.py: A = self.incidence_matrix()
combinat/designs/incidence_structures.py: #A = self.incidence_matrix()
TESTS:
The examples are nice, but marking them "random" means we're not
really testing if the function works, just that it completes. These
tests aren't perfect, but are reasonable.
::
sage: from sage.misc.sagedoc import _search_src_or_doc
sage: len(_search_src_or_doc('src', r'matrix\(', 'incidence_structures', 'self', 'combinat', interact=False).splitlines()) > 1
True
sage: 'abvar/homology' in _search_src_or_doc('doc', 'homology', 'variety', interact=False) # long time (4s on sage.math, 2012), needs sagemath_doc_html
True
sage: 'divisors' in _search_src_or_doc('src', '^ *def prime', interact=False)
True
When passing ``interactive=True``, in a terminal session this will pass the
``text/plain`` output to the configured pager, while in a notebook session
it will display the ``text/html`` output in the notebook's pager. However,
in a non-interactive session (as in the doctests) it should just print the
results to stdout::
sage: from sage.misc.sagedoc import _search_src_or_doc
sage: _search_src_or_doc('src', # long time
....: r'def _search_src_or_doc\(',
....: interact=True)
misc/sagedoc.py:...: def _search_src_or_doc(what, string, extra1='', extra2='', extra3='',
"""
interact = kwargs.get('interact', True)
path_re = kwargs.get('path_re', '')
module = kwargs.get('module', 'sage')
whole_word = kwargs.get('whole_word', False)
ignore_case = kwargs.get('ignore_case', True)
multiline = kwargs.get('multiline', False)
if what == 'src':
base_path = SAGE_SRC
if module.find('sage') == 0:
module = module[4:].lstrip(".")
base_path = os.path.join(base_path, 'sage')
module = module.replace(".", os.sep)
exts = ['py', 'pyx', 'pxd']
title = 'Source Code'
else:
module = ''
exts = ['html']
title = 'Documentation'
base_path = os.path.join(SAGE_DOC, 'html')
if not os.path.exists(base_path):
print("""Warning: the Sage documentation is not available""")
strip = len(base_path)
results = []
regexp = string
extra_regexps = extras = [extra1, extra2, extra3, extra4, extra5]
if whole_word:
regexp = r'\b' + regexp + r'\b'
extra_regexps = [r'\b%s\b' % e for e in extra_regexps]
if ignore_case:
flags = re.IGNORECASE
else:
flags = 0
for dirpath, dirs, files in os.walk(os.path.join(base_path, module)):
try:
dirs.remove('_static')
except ValueError:
pass
for f in files:
if not f.startswith('.') and re.search(r"\.(" + "|".join(exts) + ")$", f):
filename = os.path.join(dirpath, f)
if re.search(path_re, filename):
if multiline:
with open(filename) as fobj:
line = fobj.read()
if re.search(regexp, line, flags):
match_list = line
else:
match_list = None
for extra in extra_regexps:
if extra and match_list:
if not re.search(extra, match_list):
match_list = None
if match_list:
results.append(filename[strip:].lstrip("/") + '\n')
else:
with open(filename) as fobj:
match_list = [(lineno, line)
for lineno, line in enumerate(fobj)
if re.search(regexp, line, flags)]
for extra in extra_regexps:
if extra:
match_list = [s for s in match_list
if re.search(extra, s[1], re.MULTILINE | flags)]
for num, line in match_list:
results.append('{}:{}:{}'.format(
filename[strip:].lstrip('/'), num + 1, line))
text_results = ''.join(results).rstrip()
if not interact:
return text_results
html_results = format_search_as_html(title, results, [string] + extras)
from IPython.core.page import page
if not isinstance(text_results, str):
text_results = text_results.decode('utf-8', 'replace')
page({
'text/plain': text_results,
})
def search_src(string, extra1='', extra2='', extra3='', extra4='',
extra5='', **kwds):
r"""
Search Sage library source code for lines containing ``string``.
The search is case-insensitive by default.
INPUT:
- ``string`` -- a string to find in the Sage source code.
- ``extra1``, ..., ``extra5`` -- additional strings to require when
searching. Lines must match all of these, as well as ``string``.
- ``whole_word`` (default: ``False``) -- if True, search for
``string`` and ``extra1`` (etc.) as whole words only. This
assumes that each of these arguments is a single word, not a
regular expression, and it might have unexpected results if used
with regular expressions.
- ``ignore_case`` (default: ``True``) -- if False, perform a
case-sensitive search
- ``multiline`` (default: ``False``) -- if True, search more
than one line at a time. In this case, print any matching file
names, but don't print line numbers.
- ``interact`` (default: ``True``) -- if ``False``, return
a string with all the matches. Otherwise, this function returns
``None``, and the results are displayed appropriately, according
to whether you are using the notebook or the command-line
interface. You should not ordinarily need to use this.
- ``path_re`` (default: '') -- regular expression which
the filename (including the path) must match.
- ``module`` (default: 'sage') -- the module in which to
search. The default is 'sage', the entire Sage library. If
``module`` doesn't start with "sage", then the links in the
notebook output may not function.
OUTPUT: If ``interact`` is False, then return a string with all of
the matches, separated by newlines. On the other hand, if
``interact`` is True (the default), there is no output. Instead:
at the command line, the search results are printed on the screen
in the form ``filename:line_number:line of text``, showing the
filename in which each match occurs, the line number where it
occurs, and the actual matching line. (If ``multiline`` is True,
then only the filename is printed for each match.) The file paths
in the output are relative to ``$SAGE_SRC``. In the
notebook, each match produces a link to the actual file in which
it occurs.
The ``string`` and ``extraN`` arguments are treated as regular
expressions, as is ``path_re``, and errors will be raised if they
are invalid. The matches will be case-insensitive unless
``ignore_case`` is False.
.. note::
The ``extraN`` parameters are present only because
``search_src(string, *extras, interact=False)``
is not parsed correctly by Python 2.6; see http://bugs.python.org/issue1909.
EXAMPLES:
First note that without using ``interact=False``, this function
produces no output, while with ``interact=False``, the output is a
string. These examples almost all use this option, so that they
have something to which to compare their output.
You can search for "matrix" by typing ``search_src("matrix")``.
This particular search will produce many results::
sage: len(search_src("matrix", interact=False).splitlines()) # random # long time
9522
You can restrict to the Sage calculus code with
``search_src("matrix", module="sage.calculus")``, and this
produces many fewer results::
sage: len(search_src("matrix", module="sage.calculus", interact=False).splitlines()) # random
26
Note that you can do tab completion on the ``module`` string.
Another way to accomplish a similar search::
sage: len(search_src("matrix", path_re="calc", # needs sage.modules
....: interact=False).splitlines()) > 15
True
The following produces an error because the string 'fetch(' is a
malformed regular expression::
sage: print(search_src(" fetch(", "def", interact=False))
Traceback (most recent call last):
...
error: missing ), unterminated subpattern at position 6
To fix this, *escape* the parenthesis with a backslash::
sage: print(search_src(r" fetch\(", "def", interact=False)) # random # long time
matrix/matrix0.pyx: cdef fetch(self, key):
matrix/matrix0.pxd: cdef fetch(self, key)
sage: print(search_src(r" fetch\(", "def", "pyx", interact=False)) # random # long time
matrix/matrix0.pyx: cdef fetch(self, key):
As noted above, the search is case-insensitive, but you can make it
case-sensitive with the 'ignore_case' key word::
sage: s = search_src('Matrix', path_re='matrix', interact=False); s.find('x') > 0
True
sage: s = search_src('MatRiX', path_re='matrix', interact=False); s.find('x') > 0
True
sage: s = search_src('MatRiX', path_re='matrix',
....: interact=False, ignore_case=False); s.find('x') > 0
False
Searches are by default restricted to single lines, but this can
be changed by setting ``multiline`` to be True. In the following,
since ``search_src(string, interact=False)`` returns a string with
one line for each match, counting the length of
``search_src(string, interact=False).splitlines()`` gives the
number of matches. ::
sage: len(search_src('log', 'derivative', interact=False).splitlines()) < 40
True
sage: len(search_src('log', 'derivative',
....: interact=False, multiline=True).splitlines()) > 70
True
A little recursive narcissism: let's do a doctest that searches for
this function's doctests. Note that you can't put "sage:" in the
doctest string because it will get replaced by the Python ">>>"
prompt.
::
sage: print(search_src(r'^ *sage[:] .*search_src\(', interact=False)) # long time
misc/sagedoc.py:... len(search_src("matrix", interact=False).splitlines())...
misc/sagedoc.py:... len(search_src("matrix", module="sage.calculus", interact=False).splitlines())...
misc/sagedoc.py:... len(search_src("matrix", path_re="calc"...
misc/sagedoc.py:... print(search_src(" fetch(", "def", interact=False))...
misc/sagedoc.py:... print(search_src(r" fetch\(", "def", interact=False))...
misc/sagedoc.py:... print(search_src(r" fetch\(", "def", "pyx", interact=False))...
misc/sagedoc.py:... s = search_src('Matrix', path_re='matrix', interact=False); s.find('x') > 0...
misc/sagedoc.py:... s = search_src('MatRiX', path_re='matrix', interact=False); s.find('x') > 0...
misc/sagedoc.py:... s = search_src('MatRiX', path_re='matrix',...
misc/sagedoc.py:... len(search_src('log', 'derivative', interact=False).splitlines()) < 40...
misc/sagedoc.py:... len(search_src('log', 'derivative'...
misc/sagedoc.py:... print(search_src(r'^ *sage[:] .*search_src\(', interact=False))...
misc/sagedoc.py:... len(search_src("matrix", interact=False).splitlines()) > 9000...
misc/sagedoc.py:... print(search_src('matrix', 'column', 'row', 'sub',...
misc/sagedoc.py:... sage: results = search_src('format_search_as_html',...
TESTS:
As of this writing, there are about 9500 lines in the Sage library that
contain "matrix"; it seems safe to assume we will continue to have
over 9000 such lines::
sage: len(search_src("matrix", interact=False).splitlines()) > 9000 # long time
True
Check that you can pass 5 parameters::
sage: print(search_src('matrix', 'column', 'row', 'sub', 'start', 'index', interact=False)) # random # long time
matrix/matrix0.pyx:598: Get The 2 x 2 submatrix of M, starting at row index and column
matrix/matrix0.pyx:607: Get the 2 x 3 submatrix of M starting at row index and column index
matrix/matrix0.pyx:924: Set the 2 x 2 submatrix of M, starting at row index and column
matrix/matrix0.pyx:933: Set the 2 x 3 submatrix of M starting at row index and column
"""
return _search_src_or_doc('src', string, extra1=extra1, extra2=extra2,
extra3=extra3, extra4=extra4, extra5=extra5,
**kwds)
def search_doc(string, extra1='', extra2='', extra3='', extra4='',
extra5='', **kwds):
r"""
Search Sage HTML documentation for lines containing ``string``. The
search is case-insensitive by default.
The file paths in the output are relative to ``$SAGE_DOC``.
INPUT: same as for :func:`search_src`.
OUTPUT: same as for :func:`search_src`.
EXAMPLES:
See the documentation for :func:`search_src` for more examples. ::
sage: search_doc('creates a polynomial', path_re='tutorial', interact=False) # random
html/en/tutorial/tour_polynomial.html:<p>This creates a polynomial ring and tells Sage to use (the string)
If you search the documentation for 'tree', then you will get too
many results, because many lines in the documentation contain the
word 'toctree'. If you use the ``whole_word`` option, though, you
can search for 'tree' without returning all of the instances of
'toctree'. In the following, since ``search_doc('tree',
interact=False)`` returns a string with one line for each match,
counting the length of ``search_doc('tree',
interact=False).splitlines()`` gives the number of matches. ::
sage: # long time, needs sagemath_doc_html
sage: N = len(search_doc('tree', interact=False).splitlines())
sage: L = search_doc('tree', whole_word=True, interact=False).splitlines()
sage: len(L) < N
True
sage: import re
sage: tree_re = re.compile(r'(^|\W)tree(\W|$)', re.I)
sage: all(tree_re.search(l) for l in L)
True
"""
return _search_src_or_doc('doc', string, extra1=extra1, extra2=extra2,
extra3=extra3, extra4=extra4, extra5=extra5,
**kwds)
def search_def(name, extra1='', extra2='', extra3='', extra4='',
extra5='', **kwds):
r"""
Search Sage library source code for function definitions containing
``name``. The search is case-insensitive by default.
INPUT: same as for :func:`search_src`.
OUTPUT: same as for :func:`search_src`.
.. note::
The regular expression used by this function only finds function
definitions that are preceded by spaces, so if you use tabs on a
"def" line, this function will not find it. As tabs are not
allowed in Sage library code, this should not be a problem.
EXAMPLES:
See the documentation for :func:`search_src` for more examples. ::
sage: print(search_def("fetch", interact=False)) # random # long time
matrix/matrix0.pyx: cdef fetch(self, key):
matrix/matrix0.pxd: cdef fetch(self, key)
sage: print(search_def("fetch", path_re="pyx", interact=False)) # random # long time
matrix/matrix0.pyx: cdef fetch(self, key):
"""
if 'whole_word' in kwds and kwds['whole_word']:
name = r'\b' + name + r'\b'
if extra1:
extra1 = r'\b' + extra1 + r'\b'
if extra2:
extra2 = r'\b' + extra2 + r'\b'
if extra3:
extra3 = r'\b' + extra3 + r'\b'
if extra4:
extra4 = r'\b' + extra4 + r'\b'
if extra5:
extra5 = r'\b' + extra5 + r'\b'
kwds['whole_word'] = False
return _search_src_or_doc('src', '^ *[c]?def.*%s' % name, extra1=extra1,
extra2=extra2, extra3=extra3, extra4=extra4,
extra5=extra5, **kwds)
def format_search_as_html(what, results, search):
r"""
Format the output from ``search_src``, ``search_def``, or
``search_doc`` as html, for use in the notebook.
INPUT:
- ``what`` -- (string) what was searched (source code or
documentation)
- ``results`` -- (string or list) the results of the search as a string or list of
search results
- ``search`` -- (string or list) what was being searched for, either as a
string which is taken verbatim, or a list of multiple search terms if
there were more than one
This function parses ``results``: each line should have either the form
``FILENAME`` or ``FILENAME: string`` where FILENAME is the file in which
the string that matched the search was found. If FILENAME ends in '.html',
then this is part of the documentation; otherwise, it is in the source
code. In either case, an appropriate link is created.
EXAMPLES::
sage: from sage.misc.sagedoc import format_search_as_html
sage: format_search_as_html('Source', 'algebras/steenrod_algebra_element.py: an antihomomorphism: if we call the antipode `c`, then', 'antipode antihomomorphism')
'<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>'
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')
'<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>'
TESTS:
Test that results from a ``search_src`` with ``multiline=True`` works
reasonably::
sage: results = search_src('format_search_as_html', # long time
....: multiline=True, interact=False)
sage: format_search_as_html('Source', results, # long time
....: 'format_search_as_html')
'<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>'
"""
if not isinstance(search, list):
search = [search]
s = [
'<html>',
'<font color="black">',
'<h2>Search {}: {}</h2>'.format(
what, ', '.join('"{}"'.format(s) for s in search if s.strip())),
'</font>',
'<font color="darkpurple">',
'<ol>'
]
append = s.append
if not isinstance(results, list):
results = results.splitlines()
files = set()
for L in results:
filename = L.strip().split(':', 1)[0]
if filename:
files.add(filename)
files = sorted(files)
for F in files:
if F.endswith('.html'):
F = F.split('/', 2)[2]
url = '/doc/live/' + F
else:
url = '/src/' + F
append('<li><a href="%s" target="_blank"><tt>%s</tt></a>\n' % (url, F))
append('</ol>')
append('</font>')
append('</html>')
return ''.join(s)
def my_getsource(obj, oname=''):
"""
Retrieve the source code for ``obj``.
INPUT:
- ``obj`` -- a Sage object, function, etc.
- ``oname`` -- str (optional). A name under which the object is
known. Currently ignored by Sage.
OUTPUT:
Its documentation (string)
EXAMPLES::
sage: from sage.misc.sagedoc import my_getsource
sage: s = my_getsource(identity_matrix) # needs sage.modules
sage: s[15:34] # needs sage.modules
'def identity_matrix'
"""
try:
s = sageinspect.sage_getsource(obj)
return format_src(s)
except Exception as msg:
print('Error getting source:', msg)
return None
class _sage_doc:
"""
Open Sage documentation in a web browser, from either the
command-line or the notebook.
- Type "browse_sage_doc.DOCUMENT()" to open the named document --
for example, "browse_sage_doc.tutorial()" opens the tutorial.
Available documents are
- tutorial: the Sage tutorial
- reference: the Sage reference manual
- constructions: "how do I construct ... in Sage?"
- developer: the Sage developer's guide.
- Type "browse_sage_doc(OBJECT, output=FORMAT, view=BOOL)" to view
the documentation for OBJECT, as in
"browse_sage_doc(identity_matrix, 'html'). ``output`` can be
either 'html' or 'rst': the form of the output. ``view`` is
only relevant if ``output`` is ``html``; in this case, if
``view`` is True (its default value), then open up the
documentation in a web browser. Otherwise, just output the
documentation as a string.
EXAMPLES::
sage: browse_sage_doc._open("reference", testing=True)[0] # needs sagemath_doc_html
'http://localhost:8000/doc/live/reference/index.html'
sage: browse_sage_doc(identity_matrix, 'rst')[-107:-47] # needs sage.modules
'Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring'
"""
def __init__(self):
"""
EXAMPLES::
sage: browse_sage_doc._base_url
'http://localhost:8000/doc/live/'
"""
self._base_url = "http://localhost:8000/doc/live/"
self._base_path = os.path.join(SAGE_DOC, "html", "en")
def __call__(self, obj, output='html', view=True):
r"""
Return the documentation for ``obj``.
INPUT:
- ``obj`` -- a Sage object
- ``output`` -- 'html', 'rst', or 'text': return documentation in this form
- ``view`` -- only has an effect if output is 'html': in this
case, if ``view`` is ``True``, display the documentation in
a web browser. Otherwise, return the documentation as a
string.
EXAMPLES::
sage: browse_sage_doc(identity_matrix, 'rst') # needs sage.modules
"...**File:**...**Type:**...**Definition:** identity_matrix..."
sage: identity_matrix.__doc__ in browse_sage_doc(identity_matrix, 'rst') # needs sage.modules
True
sage: browse_sage_doc(identity_matrix, 'html', False) # needs sagemath_doc_html sphinx
'...div...File:...Type:...Definition:...identity_matrix...'
In the 'text' version, double colons have been replaced with
single ones (among other things)::
sage: '::' in browse_sage_doc(identity_matrix, 'rst') # needs sage.modules
True
sage: '::' in browse_sage_doc(identity_matrix, 'text') # needs sphinx
False
"""
if output != 'html' and view:
view = False
s = ''
newline = "\n\n"
try:
filename = sageinspect.sage_getfile(obj)
s += '**File:** %s' % filename
s += newline
except TypeError:
pass
obj_name = ''
locs = sys._getframe(1).f_locals
for var in locs:
if id(locs[var]) == id(obj):
obj_name = var
s += '**Type:** %s' % type(obj)
s += newline
s += '**Definition:** %s' % sageinspect.sage_getdef(obj, obj_name)
s += newline
s += '**Docstring:**'
s += newline
s += sageinspect.sage_getdoc(obj, obj_name, embedded=True)
if output == 'html':
try:
from .sphinxify import sphinxify
except ImportError:
from html import escape
html = escape(s)
else:
html = sphinxify(s)
if view:
path = os.path.join(tmp_dir(), "temp.html")
filed = open(path, 'w')
static_path = os.path.join(SAGE_DOC, "html", "en", "_static")
if os.path.exists(static_path):
title = obj_name + ' - Sage ' + sage.version.version + ' Documentation'
template = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>%(title)s</title>
<link rel="stylesheet" href="%(static_path)s/default.css" type="text/css" />
<link rel="stylesheet" href="%(static_path)s/pygments.css" type="text/css" />
<style type="text/css">
<!--
div.body {
margin: 1.0em;
padding: 1.0em;
}
div.bodywrapper {
margin: 0;
}
-->
</style>
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '',
VERSION: '%(version)s',
COLLAPSE_MODINDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: false
};
</script>
<script type="text/javascript" src="%(static_path)s/jquery.js"></script>
<script type="text/javascript" src="%(static_path)s/doctools.js"></script>
<link rel="shortcut icon" href="%(static_path)s/favicon.ico" />
<link rel="icon" href="%(static_path)s/sageicon.png" type="image/x-icon" />
</head>
<body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body">
%(html)s
</div>
</div>
</div>
</div>
</body>
</html>"""
html = template % {'html': html,
'static_path': static_path,
'title': title,
'version': sage.version.version}
filed.write(html)
filed.close()
os.system(browser() + " " + path)
else:
return html
elif output == 'rst':
return s
elif output == 'text':
try:
from .sphinxify import sphinxify
except ImportError:
return s
else:
return sphinxify(s, format='text')
else:
raise ValueError("output type {} not recognized".format(output))
def _open(self, name, testing=False):
"""
Open the document ``name`` in a web browser. This constructs
the appropriate URL and/or path name and passes it to the web
browser.
INPUT:
- ``name`` -- string, name of the documentation
- ``testing`` -- boolean (default: ``False``): if True,
then just return the URL and path-name for this document;
don't open the web browser.
EXAMPLES::
sage: browse_sage_doc._open("reference", testing=True)[0] # needs sagemath_doc_html
'http://localhost:8000/doc/live/reference/index.html'
sage: browse_sage_doc._open("tutorial", testing=True)[1] # needs sagemath_doc_html
'.../html/en/tutorial/index.html'
"""
url = self._base_url + os.path.join(name, "index.html")
path = os.path.join(self._base_path, name, "index.html")
if doc_server:
doc_server.visit(name)
return
elif os.path.exists(path):
os.system(browser() + " " + path)
else:
os.system(browser() + " " + "https://doc.sagemath.org/html/en/%s/index.html"%name)
if testing:
return (url, path)
os.system(browser() + " " + path)
def tutorial(self):
"""
The Sage tutorial. To get started with Sage, start here.
EXAMPLES::
sage: tutorial() # indirect doctest, not tested
"""
self._open("tutorial")
def reference(self):
"""
The Sage reference manual.
EXAMPLES::
sage: reference() # indirect doctest, not tested
sage: manual() # indirect doctest, not tested
"""
self._open("reference")
manual = reference
def developer(self):
"""
The Sage developer's guide. Learn to develop programs for Sage.
EXAMPLES::
sage: developer() # indirect doctest, not tested
"""
self._open("developer")
def constructions(self):
"""
Sage constructions. Attempts to answer the question "How do I
construct ... in Sage?"
EXAMPLES::
sage: constructions() # indirect doctest, not tested
"""
self._open("constructions")
browse_sage_doc = _sage_doc()
tutorial = browse_sage_doc.tutorial
reference = browse_sage_doc.reference
manual = browse_sage_doc.reference
developer = browse_sage_doc.developer
constructions = browse_sage_doc.constructions
python_help = pydoc.help
def help(module=None):
"""
If there is an argument ``module``, print the Python help message
for ``module``. With no argument, print a help message about
getting help in Sage.
EXAMPLES::
sage: help()
Welcome to Sage ...
"""
if module is not None:
python_help(module)
else:
print("""Welcome to Sage {}!
To view the Sage tutorial in your web browser, type "tutorial()", and
to view the (very detailed) Sage reference manual, type "manual()".
For help on any Sage function, for example "matrix_plot", type
"matrix_plot?" to see a help message, type "help(matrix_plot)" to see
a very similar message, type "browse_sage_doc(matrix_plot)" to view a
help message in a web browser, and type "matrix_plot??" to look at the
function's source code.
(When you type something like "matrix_plot?", "help(matrix_plot)", or
"matrix_plot??", Sage may start a paging program to display the
requested message. Type a space to scroll to the next page, type "h"
to get help on the paging program, and type "q" to quit it and return
to the "sage:" prompt.)
For license information for Sage and its components, read the file
"COPYING.txt" in the top-level directory of the Sage installation,
or type "license()".
To enter Python's interactive online help utility, type "python_help()".
To get help on a Python function, module or package, type "help(MODULE)" or
"python_help(MODULE)".""".format(sage.version.version))