This file is indexed.

/usr/share/sagemath/bin/sage-fixdoctests is in sagemath-common 7.4-9.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env python

"""
Given the output of doctest and a file, adjust the doctests so they won't fail.

Doctest failures due to exceptions are ignored.

AUTHORS::

- Nicolas M. Thiery <nthiery at users dot sf dot net>  Initial version (2008?)

- Andrew Mathas <andrew dot mathas at sydney dot edu dot au> 2013-02-14
  Cleaned up the code and hacked it so that the script can now cope with the
  situations when either the expected output or computed output are empty.
  Added doctest to sage.tests.cmdline

"""

import os, sys, difflib
import subprocess
from optparse import OptionParser
from sage.misc.temporary_file import tmp_dir

usage=r'''usage: sage --fixdoctests <source file> [doctest output] [--long]
Creates a file <source file>.out that passes the doctests (modulo any raised exceptions)'''
parser = OptionParser(usage=usage)
parser.add_option('-l','--long',dest='long', action="store_true", default=False)

options, args = parser.parse_args()

if not (len(args) in [1,2]) :
    print(usage)
    sys.exit(1)

# set input and output files
test_file = args[0]
test_output = args[1] if len(args)==2 else test_file
try:
    with open(test_file,'r') as src:
        src_in = src.read()
        pass
except IOError:
    raise ValueError('The file "%s" either does not exist or it is not readable' % test_file )
    sys.exit(1)

# put the output of the test into sage's temporary directory
doc_out = tmp_dir()+'.tmp'
os.system('sage -t %s %s > %s'%('--long' if options.long else '', test_file, doc_out))

doc_out = open(doc_out).read()
sep = "**********************************************************************\n"

doctests = doc_out.split(sep)
src_in_lines = src_in.splitlines()

for block in doctests:
    if not 'Failed example:' in block: continue  # sanity checking, but shouldn't happen

    # Extract the line, what was expected, and was got.
    line = block.find('line ')             # block should contain:  'line ##, in ...', where ## is an integer
    comma = block.find(', in ')            # we try to extract the line number which gives the test failure
    if line == -1 or comma==-1: continue   # but if something goes wrong we give up
    line_num=eval(block[line+5:comma])
    if 'Expected nothing' in block:
        exp = block.find('Expected nothing')
        block='\n'+block[exp+17:]  # so that split('\nGot:\n') does not fail below
    elif 'Expected:' in block:
        exp = block.find('Expected:\n')
        block=block[exp+10:]
    else:
        continue
    if block[-21:]=='Got:\n    <BLANKLINE>\n':
        expected=block[:-22]
        got=['']
    else:
        expected, got = block.split('\nGot:\n')
        got = got.splitlines()      # got can't be the empty string


    # If we expected nothing, and got something, then we need to insert the line before line_num
    # and match indentation with line number line_num-1
    if expected=='':
        indent = (len(src_in_lines[line_num-1]) - len(src_in_lines[line_num-1].lstrip()))
        src_in_lines[line_num-1]+='\n'+'\n'.join('%s%s'%(' '*indent,line.lstrip()) for line in got)
        continue

    # Guess how much extra indenting got needs to match with the indentation
    # of src_in_lines - we match the indentation with the line in ``got`` which
    # has the smallest indentation after lstrip(). Note that the amount of indentation
    # required could be negative if the ``got`` block is indented. In this case
    # ``indent`` is set to zero.
    indent = max(0,(len(src_in_lines[line_num]) - len(src_in_lines[line_num].lstrip())
              - min(len(got[j]) - len(got[j].lstrip()) for j in range(len(got)))))

    expected=expected.splitlines()

    # Double check that what was expected was indeed in the source file and if
    # it is not then then print a warning for the user which contains the
    # problematic lines.
    if any( expected[i].strip() != src_in_lines[line_num+i].strip() for i in range(len(expected)) ):
        import warnings
        warnings.warn("Did not manage to replace\n%s\n%s\n%s\nwith\n%s\n%s\n%s"%(
                         '>'*40,'\n'.join(expected),'>'*40,'<'*40, '\n'.join(got), '<'*40))
        continue

    # If we got something when we expected nothing then we delete the line from the
    # output, otherwise, add all of what we `got` onto the end of src_in_lines[line_num]
    if got==['']:
        src_in_lines[line_num] = None
    else:
        src_in_lines[line_num] = '\n'.join( (' '*indent + got[i]) for i in range(len(got)) )

    # Mark any remaining `expected` lines as ``None`` so as to preserve the line numbering
    for i in range(1, len(expected)):
        src_in_lines[line_num+i] = None


# Overwrite the source (or whatever output file was specified on the command line)
with open(test_output, 'w') as out:
    for line in src_in_lines:
        if line is None:
            continue
        out.write(line)
        out.write('\n')


# Show summary of changes
if test_file != test_output:
    print('The fixed doctests have been saved as {0}.'.format(test_output))
else:
    from sage.env import SAGE_ROOT
    relative = os.path.relpath(test_output, SAGE_ROOT)
    print(relative)
    if relative.startswith('..'):
        print('Fixed source file is not part of Sage.')
    else:
        subprocess.call(['git', 'diff', relative], cwd=SAGE_ROOT)