Ticket #12415: 12415_script.patch

File 12415_script.patch, 67.8 KB (added by jdemeyer, 7 years ago)

Patch to script repo; now removes old files

  • deleted file sage-doctest

    # HG changeset patch
    # User David Roe <roed.math@gmail.com>
    # Date 1332359248 25200
    # Node ID f3f3ab2c319e33a3e6f6481d6cfe0817eab9eaaa
    # Parent  6ec5fc4f65cc62da47d12f315bbe0137951560a7
    New doctesting framework.
    
    diff --git a/sage-doctest b/sage-doctest
    deleted file mode 100755
    + -  
    1 #!/usr/bin/env python
    2 
    3 ####################################################################
    4 # Doctest a single Python, Cython, Sage, TeX or ReST file, using
    5 # Sage's custom doctesting tags and framework built on top of
    6 # Python's doctest functionality.  In particular, this allows for
    7 # sage: prompts, preparsing, optional doctests, random behavior, etc.
    8 #
    9 # This also searches for TAB characters.  If any are found in .py,
    10 # .pyx, .spyx, or .sage files, then doctesting fails unless the string
    11 # "SAGE_DOCTEST_ALLOW_TABS" is also present somewhere (anywhere!) in
    12 # the file.
    13 #
    14 # Return value in process exit code:
    15 # 0: all tests passed
    16 # 1: file not found
    17 # 2: KeyboardInterrupt
    18 # 4: doctest process was terminated by a signal
    19 # 8: the doctesting framework raised an exception
    20 # 16: script called with bad options
    21 # 32: (used internally in sage-ptest)
    22 # 64: time out
    23 # 128: failed doctests
    24 ####################################################################
    25 
    26 # System imports
    27 import os, re, sys, signal, time, shutil, tempfile, random
    28 
    29 # if any of the following environment variables are set, use them for
    30 # the timeout lengths.
    31 TIMEOUT = os.getenv('SAGE_TIMEOUT')
    32 TIMEOUT_LONG = os.getenv('SAGE_TIMEOUT_LONG')
    33 TIMEOUT_VALGRIND = os.getenv('SAGE_TIMEOUT_VALGRIND')
    34 
    35 if TIMEOUT is None:
    36     # the default timeout for doctests: 6 minutes (in seconds)   
    37     TIMEOUT = 6 * 60
    38 else:
    39     # output from os.getenv is a string: convert to a number
    40     TIMEOUT = float(TIMEOUT)
    41    
    42 if TIMEOUT_LONG is None:
    43     # the timeout value for long doctests: 30 minutes (in seconds)
    44     TIMEOUT_LONG = 30 * 60
    45 else:
    46     TIMEOUT_LONG = float(TIMEOUT_LONG)
    47    
    48 if TIMEOUT_VALGRIND is None:
    49     # the timeout for doctests running under valgrind tools: unreasonably long
    50     TIMEOUT_VALGRIND = 1024 * 1024
    51 else:
    52     TIMEOUT_VALGRIND = float(TIMEOUT_VALGRIND)
    53 
    54 long_time          = False
    55 only_optional      = False
    56 only_optional_tags = set([])
    57 optional           = False
    58 verbose            = False
    59 random_order       = False         # default is that tests aren't run in random order
    60 
    61 argv = sys.argv
    62 
    63 import sage.misc.preparser
    64 
    65 ######################################################
    66 # This code is copied from sage.misc.misc for speed:
    67 ######################################################
    68 def is_64bit():
    69     return sys.maxint == 9223372036854775807
    70 
    71 ######################################################
    72 # Temporary files
    73 ######################################################   
    74 DOT_SAGE = os.environ['DOT_SAGE']
    75 if 'SAGE_TESTDIR' not in os.environ or os.environ['SAGE_TESTDIR'] is "":
    76     SAGE_TESTDIR = os.path.join(DOT_SAGE, "tmp")
    77 else:
    78     SAGE_TESTDIR = os.environ['SAGE_TESTDIR']
    79 
    80 tmpfiles = [] # list of temporary files to be deleted if doctesting succeeds
    81 
    82 def delete_tmpfiles():
    83     for f in tmpfiles:
    84         try:
    85             os.remove(f)
    86         except OSError:
    87             pass
    88    
    89 ######################################################
    90 # Set environment variables
    91 ######################################################   
    92 SAGE_ROOT = os.environ["SAGE_ROOT"]
    93 LD = os.environ["LD_LIBRARY_PATH"]
    94 os.environ["LD_LIBRARY_PATH"] = os.path.join(SAGE_ROOT, 'local',
    95                                              'lib') + ":" + LD
    96 os.environ["PYTHONPATH"] = os.path.join(SAGE_ROOT, 'local', 'lib', 'python',
    97                                         'site-packages')
    98 if os.environ.has_key('SAGE_PATH'):
    99     os.environ["PYTHONPATH"] = os.environ["PYTHONPATH"] + ':' + os.environ['SAGE_PATH']
    100 
    101 
    102 ######################################################
    103 # Custom flags for the valgrind modes
    104 ######################################################
    105 logfile = ' --log-file=' + os.path.join(DOT_SAGE, 'valgrind', 'sage-%s') + ' '
    106 try:
    107     SAGE_MEMCHECK_FLAGS = os.environ['SAGE_MEMCHECK_FLAGS']
    108     print SAGE_MEMCHECK_FLAGS
    109 except:
    110     suppfile = os.path.join('$SAGE_LOCAL', 'lib', 'valgrind', 'sage.supp')
    111     SAGE_MEMCHECK_FLAGS = " --leak-resolution=high %s --leak-check=full --num-callers=25 --suppressions=%s " % (logfile % 'memcheck.%p', suppfile)
    112 
    113 try:
    114     SAGE_MASSIF_FLAGS = os.environ['SAGE_MASSIF_FLAGS']
    115 except:
    116     SAGE_MASSIF_FLAGS = " --depth=6 " + (logfile % 'massif.%p')
    117 
    118 try:
    119     SAGE_CALLGRIND_FLAGS = os.environ['SAGE_CALLGRIND_FLAGS']
    120 except:
    121     SAGE_CALLGRIND_FLAGS = logfile % 'callgrind.%p'
    122 
    123 try:
    124     SAGE_CACHEGRIND_FLAGS = os.environ['SAGE_CACHEGRIND_FLAGS']
    125 except:
    126     SAGE_CALLGRIND_FLAGS = logfile % 'cachegrind.%p'
    127 
    128 try:
    129     SAGE_OMEGA_FLAGS = os.environ['SAGE_OMEGA_FLAGS']
    130 except:
    131     SAGE_OMEGA_FLAGS = logfile % 'omega.%p'
    132 
    133 ######################################################
    134 # The Python binary
    135 ######################################################
    136 PYTHON = "python"
    137 
    138 
    139 def pad_zeros(s, size):
    140     """
    141     INPUT:
    142        - `s` -- something coercible to string
    143        - `size` -- integer
    144 
    145     Return s as a string with 0's padded on the left so the string has
    146     length exactly size.
    147     """
    148     s = str(s)
    149     return "0"*(size-len(s)) + s
    150 
    151 index_cache = set([])
    152 def new_index(n):
    153     """
    154     Return n-th example number.  If random_order is set, this is a randomly
    155     chosen number that we haven't returned before.  If random_order is not
    156     set, this just returns n.
    157 
    158     INPUT:
    159        - `n` -- integer
    160     """
    161     if random_order:
    162         while True:
    163             a = random.randint(0,2**32)
    164             if a not in index_cache:
    165                 index_cache.add(a)
    166                 return a
    167     else:
    168         return n
    169 
    170 def test_code(filename):
    171     # Process exit codes for the generated doctest runner script:
    172     # 0: everything passed
    173     # 1-253: number of failed doctests
    174     # 254: >= 254 doctests failed
    175     # 255: exception raised by doctesting framework
    176     dict = { 'DIR'             : repr(os.path.join(SAGE_ROOT, 'local', 'bin')),
    177              'FILENAME'        : repr(filename),
    178              'OUTPUT_FILENAME' : repr(filename + '.timeit.sobj'),
    179              'TIMEIT'          : do_timeit, # global
    180              'VERBOSE'         : verbose }
    181     return """
    182 if __name__ ==  '__main__':
    183     verbose = %(VERBOSE)s
    184     do_timeit = %(TIMEIT)s
    185     output_filename = %(OUTPUT_FILENAME)s
    186 
    187     import sys
    188     sys.path = sys.path + [%(DIR)s]
    189     import sagedoctest
    190 
    191     # execfile(%(FILENAME)s)
    192     m = sys.modules[__name__]
    193     m.__file__ = %(FILENAME)s
    194 
    195     try:
    196 
    197         # configure special sage doc test runner
    198         runner = sagedoctest.SageDocTestRunner(checker=None, verbose=verbose, optionflags=0)
    199         runner._collect_timeit_stats = do_timeit
    200         runner._reset_random_seed = True
    201 
    202         runner = sagedoctest.testmod_returning_runner(m,
    203                        # filename=%(FILENAME)s,
    204                        verbose=verbose,
    205                        globs=globals(),
    206                        runner=runner)
    207         runner.save_timeit_stats_to_file_named(output_filename)
    208     except:
    209         quit_sage(verbose=False)
    210         import traceback
    211         traceback.print_exc(file=sys.stdout)
    212         sys.exit(255)
    213     quit_sage(verbose=False)
    214     if runner.failures > 254:
    215         sys.exit(254)
    216     sys.exit(runner.failures)
    217 """ % dict
    218 
    219 NONE=0; LONG_TIME=1; RANDOM=2; OPTIONAL=3; NOT_IMPLEMENTED=4; NOT_TESTED=5; TOLERANCE=6
    220 tolerance_pattern = re.compile(r'\b((?:abs(?:olute)?)|(?:rel(?:ative)?))? *?tol(?:erance)?\b( +[0-9.e+-]+)?')
    221 
    222 def comment_modifier(s):
    223     sind = s.find('#')
    224     if sind == -1 or s[sind:sind+3] == '###':
    225         return [], ''
    226     eind = s.find('###',sind+1)
    227     L = s[sind+1:eind].lower()
    228     v = []
    229     if 'optional' in L:
    230         v.append(OPTIONAL)
    231     if 'known bug' in L:
    232         # Doctests marked like this should be automatically converted
    233         # to "optional bug", so they will be run by
    234         #
    235         #    sage -t ... --only-optional=bug
    236         #
    237         # So replace 'known' with '' or 'optional', depending on
    238         # whether 'optional' is already there.
    239         if 'optional' in L:
    240             L = L.replace('known', '')
    241         else:
    242             v.append(OPTIONAL)
    243             L = L.replace('known', 'optional')
    244     if 'long time' in L:
    245         v.append(LONG_TIME)
    246     if 'not implemented' in L:
    247         v.append(NOT_IMPLEMENTED)
    248     if 'not tested' in L:
    249         v.append(NOT_TESTED)
    250     if 'random' in L:
    251         v.append(RANDOM)
    252     m = tolerance_pattern.search(L)
    253     if m:
    254         v.append(TOLERANCE)
    255         rel_or_abs, epsilon = m.groups()
    256         if rel_or_abs is not None:
    257             rel_or_abs = rel_or_abs[:3]
    258         if epsilon is None:
    259             epsilon = 1e-15
    260         else:
    261             epsilon = float(epsilon.strip())
    262         v.append((rel_or_abs, epsilon))
    263     return v, L
    264 
    265 def close_tolerance(comment_tags):
    266     rel_or_abs, epsilon = comment_tags[comment_tags.index(TOLERANCE) + 1]
    267     return "... ''', res, %r%s)" % (epsilon, '' if rel_or_abs is None else ", '%s'" % rel_or_abs)
    268 
    269 def preparse_line_with_prompt(L):
    270     i = L.find(':')
    271     if i == -1:
    272         return sage.misc.preparser.preparse(L)
    273     else:
    274         return L[:i+1] + sage.misc.preparser.preparse(L[i+1:])
    275 
    276 def doc_preparse(s):
    277     """
    278     Run the preparser on the documentation string s.
    279     This *only* preparses the input lines, i.e., those
    280     that begin with "sage:".or with "..."
    281     """
    282     sl = s.lower()
    283 
    284     # t: Deal with code whose output should be ignored.
    285     t = []
    286 
    287     # used for adapting the output based on comments
    288     has_tolerance = False
    289     comment_modifiers = []
    290     last_prompt_comment = ''
    291    
    292     for L in s.splitlines():
    293         if not L.strip():
    294             if has_tolerance:
    295                 t.append(close_tolerance(c) + old_cmd)
    296                 has_tolerance = False
    297        
    298         begin = L.lstrip()[:5]
    299         comment = ''
    300         if begin == 'sage:':
    301             if has_tolerance:
    302                 # old_cmd contains the command being tested along with
    303                 # the line number.
    304                 t.append(close_tolerance(c) + old_cmd)
    305             c, comment = comment_modifier(L)
    306             last_prompt_comment = comment
    307             line = ''
    308             if LONG_TIME in c and not long_time:
    309                 L = '\n'  # extra line so output ignored
    310             if RANDOM in c:
    311                 L = L.replace('sage:', 'sage: print "ignore this"; ')
    312             if NOT_TESTED in c:
    313                 L = '\n'   # not tested
    314             if NOT_IMPLEMENTED in c:
    315                 L = '\n'   # not tested
    316             if OPTIONAL in c and not (only_optional or optional):
    317                 L = '\n'
    318             if TOLERANCE in c:
    319                 t.append(">>> res = Exception")
    320                 L = "sage: res = %s" % L.lstrip()[5:]
    321                 has_tolerance = True
    322             else:
    323                 has_tolerance = False
    324             line = preparse_line_with_prompt(L)
    325             if RANDOM in c:
    326                 # count spaces at the beginning of line to fix alignment later
    327                 i = 0
    328                 while i < len(line) and line[i].isspace():
    329                     i += 1
    330                 # append a line saying 'ignore' followed by ellipsis (...)
    331                 # and an empty line, to ignore the output given in the test
    332                 line += '\n' + ' '*i + 'ignore ...\n'
    333             if has_tolerance:
    334                 # save the current command along with the line number
    335                 try:
    336                     idx = line.index('###_sage"line')
    337                     old_cmd = line[idx:]
    338                 except IndexError:
    339                     old_cmd = ''
    340                 line += "\n>>> check_with_tolerance('''"
    341             t.append(line)
    342            
    343         elif begin.startswith('...'):
    344             comment = last_prompt_comment
    345             i = L.find('.')
    346             t.append(L[:i+3] + sage.misc.preparser.preparse(L[i+3:]))
    347            
    348         else:
    349             comment = last_prompt_comment
    350             if has_tolerance:
    351                 L = "... " + L
    352             t.append(L)
    353        
    354         comment_modifiers.append(comment)
    355         if has_tolerance:
    356             # In this case, we added a line to the source code:
    357             #      ">>> res = Exception"
    358             # So we need to add 'comment' one more time to
    359             # comment_modifiers so that the length of the list of
    360             # lines agrees with the length of comment_modifiers.
    361             comment_modifiers.append(comment)
    362 
    363     if has_tolerance:
    364         t.append(close_tolerance(c))
    365 
    366     # The very last line -- which is typically """ -- must never be marked as optional,
    367     # or it might not get included, which would be a syntax error.
    368     comment_modifiers[-1] = ''
    369 
    370     # if only_optional is True, then we skip the entire block unless certain
    371     # conditions are met:
    372     #  1. If the tag list is empty, some line must contain # optional
    373     #  2. If the tag list is nonempty, some line must contain
    374     #                 # optional - nonempty,subset,of,tags
    375     #     In this case, we also strip out all #optional lines that don't
    376     #     contain a nonempty subset of tags, but keep all other lines.
    377     if only_optional:
    378         # check if any doctest is optional
    379         contains_optional_doctests = (len([x for x in comment_modifiers if 'optional' in x]) > 0)
    380         if len(only_optional_tags) == 0:
    381             # case 1
    382             if not contains_optional_doctests:
    383                 t = []
    384         else:
    385             # case 2
    386             # first test that some line contains nonempty subset of tags, since
    387             # if not we won't bother at all.
    388             if contains_optional_doctests:
    389                 # make a list of each optional line cut out by our tag choices
    390                 v = [i for i in range(len(t)) if only_optional_include(comment_modifiers[i])]
    391                 if len(v) == 0:
    392                     # if v is empty, don't test this block at all.
    393                     t = []
    394                 else:
    395                     # otherwise, we test everything that is non-optional along with everything
    396                     # listed in v.
    397                     v = set(v)
    398                     t = [t[i] for i in range(len(t)) if  i in v  or  'optional' not in comment_modifiers[i]]
    399             else:
    400                 t = []
    401     # Now put docstring together and return it.
    402    
    403     return '\n'.join(t)
    404 
    405 def only_optional_include(s):
    406     """
    407     Return True if s is of the form
    408           # optional list,of,tags
    409     and the list of tags is a nonempty subset the global list only_optional_tags.
    410     If only_optional_tags is empty, then it is considered to be the
    411     set of all tags.
    412 
    413     INPUT:
    414         s -- a string
    415         only_optional_tags -- a global variable
    416     OUTPUT:
    417         bool
    418     """
    419     i = s.find('optional')
    420     if i == -1:
    421         return False
    422     if len(only_optional_tags) == 0:
    423         # This doctest has # optional in it, but there are no tags, which
    424         # mean test everything that is optional.
    425         return True
    426     # Delete all white space, colons, periods, commas, and hyphens:
    427     s = ','.join(s[i+len('optional')+1:].translate(None, '-:,.').split())
    428     v = set(s.split(','))
    429     # Delete 'needs' and 'requires'.
    430     v.discard('needs')
    431     v.discard('requires')
    432     return (len(v) > 0 and v.issubset(only_optional_tags))
    433 
    434 
    435 def extract_doc(file_name, library_code=False):
    436     """
    437     INPUT:
    438         file_name -- string; name of the file to extract the
    439                      docstrings from.
    440         library_code -- bool (default: False); if True, this file is
    441                     Sage core library code, so we do not import it,
    442                     and do doctest in a temporary directory.
    443     """
    444     F = open(file_name).readlines()
    445     # Put line numbers on every input line
    446     v = []
    447     i = 1
    448     j = 0
    449     while j < len(F):
    450         L = F[j].rstrip()
    451         if L.lstrip()[:5] == 'sage:':
    452             while j < len(F) and L.endswith('\\') and not L.endswith('\\\\'):
    453                 j += 1
    454                 i += 1
    455                 L = L[:-1] + F[j].lstrip().lstrip('...').rstrip()
    456             L += '###_sage"line %s:_sage_    %s_sage"'%(i, L.strip())
    457         j += 1
    458         i += 1
    459         v.append(L)
    460 
    461     # 32/64-bit.  If we're on a 32-bit computer, remove all lines that
    462     # contains "# 64-bit", and if we're on a 64-bit machine remove all
    463     # lines that contain "# 32-bit".  This makes it possible to have
    464     # different output in the doctests for different bit machines.
    465    
    466     if is_64bit():
    467         exclude_string = "# 32-bit"
    468     else:
    469         exclude_string = "# 64-bit"
    470        
    471     F = '\n'.join([L for L in v if not exclude_string in L])
    472 
    473     if is_64bit():
    474         F = F.replace('# 64-bit','')
    475     else:
    476         F = F.replace('# 32-bit','')
    477    
    478     F = F.replace('\'"""\'','')
    479 
    480     root_name, ext = os.path.splitext(file_name)
    481     if ext == ".tex":
    482         F = pythonify_tex(F)
    483     elif ext == ".rst":
    484         F = pythonify_rst(F)
    485  
    486     s = "# -*- coding: utf-8 -*-\n"
    487     s += """
    488 # This file was generated by 'sage-doctest' from
    489 # '%s'.
    490 
    491 """  % os.path.join(os.getcwd(), file_name)
    492     s += "from sage.all_cmdline import *; \n"
    493     s += "import sage.misc.displayhook\n"
    494     s += "import sys\n"
    495     s += "sys.displayhook = sage.misc.displayhook.DisplayHook(sys.displayhook)\n"
    496     s += "import sage.plot.plot; sage.plot.plot.DOCTEST_MODE=True\n"  # turn off image popup
    497     s += r"""
    498 def warning_function(f):
    499     import warnings
    500 
    501     def doctest_showwarning(message, category, filename, lineno, file=f, line=None):
    502         try:
    503             file.write(warnings.formatwarning(message, category, 'doctest', lineno, line))
    504         except IOError:
    505             pass # the file (probably stdout) is invalid
    506     return doctest_showwarning
    507 
    508 def change_warning_output(file):
    509     import warnings
    510     warnings.showwarning = warning_function(file)
    511 
    512 import re
    513 float_pattern = re.compile('[+-]?((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?')
    514 
    515 def check_with_tolerance(expected, actual, epsilon, rel_or_abs=None):
    516     if actual is Exception:
    517         return # error computing actual
    518     else:
    519         actual = str(actual)
    520     expected = re.sub('\n +', '\n', expected)
    521     actual = re.sub('\n +', '\n', actual)
    522     assert float_pattern.sub('#', expected.strip()) == float_pattern.sub('#', actual.strip()), \
    523         "Expected '" + expected + "' got '" + actual + "'"
    524     for expected_value, actual_value in zip(float_pattern.finditer(expected), float_pattern.finditer(actual)):
    525         expected_value = float(expected_value.group())
    526         actual_value = float(actual_value.group())
    527         if rel_or_abs == 'abs' or expected_value == 0:
    528             assert abs(expected_value - actual_value) < epsilon, "Out of tolerance %s vs %s" % (expected_value, actual_value)
    529         else:
    530             assert abs((expected_value - actual_value) / expected_value) < epsilon, "Out of tolerance %s vs %s" % (expected_value, actual_value)
    531 """
    532 
    533     if not library_code:
    534         if ext in ['.pyx','.spyx']:
    535             s += "cython(open('%s').read())\n\n" % file_name
    536 
    537         elif ext in ['.py', '.sage']:
    538 
    539             # For non-libary files, we need two different Python
    540             # files: one which contains the original Python code,
    541             # which we import, and one which contains the doctesting
    542             # framework.  First for FILE.py or FILE.sage, we replace
    543             # FILE with FILE_PID.  Then for FILE.sage, the imported
    544             # file is the preparsed version, so call that
    545             # FILE_PID_preparsed.py.  For FILE.py, the imported
    546             # version is just a copy of the original file, called
    547             # FILE_PID_orig.py.  The file containing doctesting
    548             # framework will be FILE_PID.py; this gets run through the
    549             # actual doctesting procedure by the 'test_file' function
    550             # below.
    551  
    552             root_name = os.path.basename(root_name)
    553             target_name = "%s_%d" % (root_name, os.getpid()) # like 'root_name', but unique
    554             target_base = os.path.join(SAGE_TESTDIR, target_name) # like 'target_name' but with full path
    555 
    556             if ext == '.sage':
    557                 # TODO: preparse "<file>.sage" with a Sage library call
    558                 # instead and write a string into temp_name.
    559 
    560                 # For now: "sage -preparse <file>.sage" doesn't have any
    561                 # output options and always creates <file>.py in the same
    562                 # directory, so we first copy the *source* into SAGE_TESTDIR:
    563                 os.system("cp '%s' %s.sage" % (file_name, target_base))
    564                 # Now create SAGE_TESTDIR/<target_name>.py:
    565                 os.system("sage -preparse %s.sage" % target_base)
    566                 os.system("mv '%s'.py %s_preparsed.py" % (target_base, target_base))
    567                 tmpfiles.append(target_base + ".sage")
    568                 s += "\nfrom %s_preparsed import *\n\n" % target_name
    569                 tmpfiles.append(target_base + "_preparsed.py") # preparsed version of original
    570                 tmpfiles.append(target_base + "_preparsed.pyc") # compiled version
    571             else:
    572                 # TODO: instead of copying the file, add its source
    573                 # directory to PYTHONPATH.  We would also have to
    574                 # import from 'name' instead of 'target_name'.
    575                 os.system("cp '%s' %s_orig.py" % (file_name, target_base))
    576                 s += "\nfrom %s_orig import *\n\n" % target_name
    577                 tmpfiles.append(target_base + "_orig.py") # copied original
    578                 tmpfiles.append(target_base + "_orig.pyc") # compiled version
    579 
    580             tmpfiles.append(target_base + ".py") # file with doctesting framework
    581             tmpfiles.append(target_base + ".pyc") # compiled version
    582 
    583     # Prefix/suffix for all doctests replacing the starting/ending """
    584     doc_prefix = 'r""">>> set_random_seed(0L)\n\n>>> change_warning_output(sys.stdout)\n\n'
    585     doc_suffix = '\n>>> sig_on_count()\n0\n"""'
    586 
    587     n = 0
    588     while True:
    589         i = F.find('"""')
    590         if i == -1: break
    591         k = F[i+3:].find('"""')
    592         if k == -1: break
    593         j = i+3 + k
    594         try:
    595             doc = doc_preparse(F[i:j+3])
    596         except SyntaxError:
    597             doc = F[i:j+3]
    598         if len(doc):
    599             doc = doc_prefix + doc[3:-3] + doc_suffix
    600             if random_order:
    601                 n_str = pad_zeros(new_index(n),10)
    602             else:
    603                 n_str = str(n)
    604             s += "def example_%s():" % n_str
    605             n += 1
    606             s += "\t"+ doc + "\n\n"
    607         F = F[j+3:]
    608 
    609     if n == 0:
    610         return  ''
    611     s += test_code(os.path.abspath(file_name))
    612 
    613     # Allow for "sage:" instead of the traditional Python ">>>".
    614     prompt_re = re.compile(r'(\n[ \t]*)sage:', re.MULTILINE)
    615     s = prompt_re.sub(r'\1>>>', s)
    616     s = s.replace('_sage"','')
    617    
    618     return s
    619 
    620 def pythonify_tex(F):
    621     """
    622     INPUT:
    623         F -- string; read in latex file
    624     OUTPUT:
    625         string -- python program that has functions with docstrings made from the
    626                   verbatim examples in the latex file.
    627     """
    628     # Close links:
    629     F = F.replace('\\end{verbatim}%link','')
    630     F = F.replace('%link\n\\begin{verbatim}','')
    631    
    632     # Get rid of skipped code
    633     s = ''
    634     while True:
    635         i = F.find('%skip')
    636         if i == -1:
    637             s += F
    638             break
    639         s += F[:i]
    640         F = F[i:]
    641         j = F.find('\\end{verbatim}')
    642         if j == -1:
    643             break
    644         F = F[j + len('\\end{verbatim}')+1:]
    645     F = s
    646    
    647     # Make the verbatim environ's get extracted via the usual parser above
    648     F = F.replace("\\begin{verbatim}",'"""')
    649     F = F.replace("\\end{verbatim}",'"""')
    650     return F
    651 
    652 def pythonify_rst(F):
    653     """
    654     INPUT:
    655         F -- string; read in ReST file
    656     OUTPUT:
    657         string -- python program that has functions with docstrings made from the
    658                   verbatim examples in the ReST file.
    659     """
    660     import re
    661 
    662     link_all = re.search(r'^\s*\.\.\s+linkall\s*$', F, re.M)
    663 
    664     def get_next_verbatim_block(s, pos):
    665         while True:
    666             # regular expression search in string s[pos:] for:
    667             # whitespace followed by "::" at the start of a line
    668             srch = re.search('^(\s*).*::\s*$', s[pos:], re.M)
    669 
    670             #Return -1 if we don't find anything.
    671             if srch is None:
    672                return "", -1, False
    673 
    674             pos += srch.start()
    675 
    676             prev_line = F.rfind("\n", 0, pos)
    677             start_of_options = F.rfind("\n", 0, prev_line if prev_line != -1 else 0)
    678             options = F[start_of_options:pos]
    679 
    680             pos += (srch.end() - srch.start())
    681 
    682             #Don't return skipped blocks
    683             if '.. skip' in options:
    684                 continue
    685 
    686             #Check to see if we need to link up with the previous
    687             #block
    688             link_previous = link_all or ('.. link' in options)
    689 
    690             #Find the first line that isn't indented as much as
    691             #whatever followed the double colon.  The whitespace before
    692             #the double colon is stored in srch.groups()[0]
    693             lw = len(srch.groups()[0])  # length of whitespace before ::
    694             #So match anything of the form
    695             # beg. of line + whitespace of length at most lw + nonwhitespace
    696             no_indent = re.search("^\s{,%s}\S"%lw, s[pos:], re.M)
    697 
    698             if no_indent is None:
    699                 return s[pos:], None, link_previous
    700 
    701             block, pos = s[pos:pos+no_indent.start()], pos+no_indent.start()
    702             if re.compile('^\s*sage: ', re.M).search(block) is not None:
    703                 return block, pos, link_previous
    704 
    705     s = ''
    706     pos = 0
    707     while pos is not None and pos != -1:
    708         block, pos, link_previous = get_next_verbatim_block(F, pos)
    709 
    710         #Break if we can't find any more verbatim blocks
    711         if pos == -1:
    712             break
    713 
    714         #Check to see if we have to link this block
    715         #up with the previous block
    716         if link_previous and s != "":
    717             #Get rid of the trailing quotes and newlines from the
    718             #previous block
    719             s = s[:-5]
    720 
    721             s += '%s\n"""\n\n'%block
    722         else:
    723             s += '\n"""\n%s\n"""\n\n'%block
    724 
    725     #print s
    726     #print "--"*100
    727     return s
    728 
    729    
    730 
    731 def post_process(s, file, tmpname):
    732     s = s.replace('.doctest_','')
    733     if file[-3:] == 'pyx':
    734         s = s.replace('"%s", line'%file[:-1], '"%s", line'%file)
    735     i = s.find("Failed example:")
    736     cnt = 0
    737     while i != -1:
    738         k = s[:i].rfind('File')
    739         k += s[k:].find(',')
    740         j = s[i:].find('###line')
    741         s = s[:k] + ', ' + s[i+j+3:]
    742         i = s.find("Failed example:")
    743         if i != -1:
    744             t = s[:i]
    745         else:
    746             t = s
    747         if t.find('check_with_tolerance') != -1:
    748             j = s.find('Exception raised')
    749             ass = 'AssertionError: '
    750             k = s.find(ass)
    751             s = s[:j] + s[k+len(ass):]
    752         i = s.find("Failed example:")
    753         cnt += 1
    754         if cnt > 1000:
    755             break
    756     s = s.replace(':_sage_',':\n').replace('>>>','sage:')
    757     c = '###line [0-9]*\n'
    758     r = re.compile(c)
    759     s = r.sub('\n',s)
    760     if cnt > 0:
    761         s += "For whitespace errors, see the file %s"%tmpname
    762     return (s, cnt)
    763 
    764 
    765 def test_file(file, library_code):
    766     if os.path.exists(file):
    767         s = extract_doc(file, library_code=library_code)
    768         if len(s) == 0:
    769             delete_tmpfiles()
    770             sys.exit(0)
    771 
    772         name = os.path.basename(file)
    773         name = name[:name.find(".")]
    774         f = os.path.join(SAGE_TESTDIR, "%s_%d.py" % (name, os.getpid()))
    775 
    776         open(f,"w").write(s)
    777         tmpfiles.append(f)
    778 
    779         cmd = "%s %s"%(PYTHON, f)
    780         if gdb:
    781             print "*"*80
    782             print "Type r at the (gdb) prompt to run the doctests."
    783             print "Type bt if there is a crash to see a traceback."
    784             print "*"*80
    785             cmd = "gdb --args " + cmd
    786 
    787         if memcheck:
    788             cmd = "valgrind --tool=memcheck " + SAGE_MEMCHECK_FLAGS + cmd
    789         if massif:
    790             cmd = "valgrind --tool=massif " + SAGE_MASSIF_FLAGS + cmd
    791         if cachegrind:
    792             cmd = "valgrind --tool=cachegrind " +  SAGE_CACHEGRIND_FLAGS + cmd
    793         if omega:
    794             cmd = "valgrind --tool=exp-omega " + SAGE_OMEGA_FLAGS + cmd
    795 
    796         VALGRIND = os.path.join(DOT_SAGE, 'valgrind')
    797         try:
    798             os.makedirs(VALGRIND)
    799         except OSError:
    800             if not os.path.isdir(VALGRIND):
    801                 raise
    802 
    803         tm = time.time()
    804         try:
    805             out = ''; err = ''
    806             if verbose or gdb or memcheck or massif or cachegrind:
    807                 import subprocess
    808                 proc = subprocess.Popen(cmd, shell=True)
    809                 while time.time()-tm <= TIMEOUT and proc.poll() == None:
    810                     time.sleep(0.1)
    811                 if time.time()-tm >=TIMEOUT:
    812                     os.kill(proc.pid, 9)
    813                     print "*** *** Error: TIMED OUT! PROCESS KILLED! *** ***"
    814                 e = proc.poll()
    815             else:
    816                 outf = tempfile.NamedTemporaryFile()
    817                 import subprocess
    818                 proc = subprocess.Popen(cmd, shell=True, \
    819                         stdout=outf.file.fileno(), stderr = outf.file.fileno())
    820                 while time.time()-tm <= TIMEOUT and proc.poll() == None:
    821                     time.sleep(0.1)
    822                 if time.time()-tm >=TIMEOUT:
    823                     os.kill(proc.pid, 9)
    824                     print "*** *** Error: TIMED OUT! PROCESS KILLED! *** ***"
    825                 outf.file.seek(0)
    826                 out = outf.read()
    827                 e = proc.poll()
    828         except KeyboardInterrupt:
    829             # TODO: if tests were interrupted but there were no failures, delete tmpfiles.
    830             print "KeyboardInterrupt -- interrupted after %.1f seconds!" % (time.time()-tm)
    831             sys.exit(2)
    832         if 'raise KeyboardInterrupt' in err:
    833             # TODO: if tests were interrupted but there were no failures, delete tmpfiles.
    834             print "*"*80 + "Control-C pressed -- interrupting doctests." + "*"*80
    835             sys.exit(2)
    836 
    837         if time.time() - tm >= TIMEOUT:
    838             print err
    839             sys.exit(64)
    840 
    841         s, numfail = post_process(out, file, f)
    842         s += err
    843 
    844         # search for tabs.  if found, the doctest fails, unless the string
    845         # "SAGE_DOCTEST_ALLOW_TABS" is also present somewhere in the file.
    846         ext = os.path.splitext(file)[1]
    847         if ext in [".py", ".pyx", ".sage", ".spyx"]:
    848             ff = open(file)
    849             source = ff.read()
    850             ff.close()
    851             if (source.find("SAGE_DOCTEST_ALLOW_TABS") == -1
    852                 and source.find("\t") != -1):
    853                 numfail += 1
    854                 s = "*"*70 + "\n" + "Error: TAB character found.\n" + s
    855         if e == 255:
    856             # The doctesting code raised an exception
    857             print "Exception raised by doctesting framework. Use -verbose for details."
    858             sys.exit(8)
    859 
    860         if numfail == 0 and e > 0:
    861             numfail = e
    862         if numfail > 0:
    863             if not (verbose or gdb or memcheck or massif or cachegrind):
    864                 print s
    865             sys.exit(128)
    866         elif e < 0:
    867             print "The doctested process was killed by signal %s" % (-e)
    868             sys.exit(4)
    869         else:
    870             delete_tmpfiles()
    871             sys.exit(0)
    872     else:
    873         print "Error running %s, since file %s does not exist."%(
    874             argv[0], argv[1])
    875         sys.exit(1)
    876 
    877 
    878 def has_opt(opt):
    879     if '-' + opt in argv:
    880         i = argv.index('-' + opt)
    881         del argv[i]
    882         return True
    883     elif '--' + opt in argv:
    884         i = argv.index('--' + opt)
    885         del argv[i]
    886         return True
    887     return False
    888 
    889 def parse_only_opt():
    890     """
    891     Search through argv for the -only-optional=list,of,tags option.
    892     If it is there, return True and a set of tags as a list of strings.
    893     If not, return False and an empty set.
    894 
    895     NOTE: Because it's very natural/easy to type only_optional instead
    896     of only-optional as an option (given that variable names have
    897     underscores in Python), using only_optional is accepted in
    898     addition to only-optional.
    899 
    900     OUTPUT:
    901         bool, set
    902     """
    903     for j, X in enumerate(argv):
    904         Z = X.lstrip('-')
    905         if Z.startswith('only-optional=') or Z.startswith('only_optional='):
    906             i = Z.find('=')
    907             del argv[j]
    908             return True, set(Z[i+1:].lower().split(','))
    909         elif Z.startswith('only-optional') or Z.startswith('only_optional'):  # no equals
    910             del argv[j]
    911             return True, set([])
    912     return False, []
    913 
    914 def parse_rand():
    915     """
    916     Randomize seed and return True if we randomize doctest order.
    917     Otherwise we leave doctests in the "traditional order" and return False.
    918     """
    919     for j, X in enumerate(argv):
    920         Z = X.lstrip('-')
    921         if Z.startswith('randorder'):
    922             del argv[j]
    923             i = Z.find('=')
    924             if i != -1:
    925                 random.seed(Z[i+1:])
    926             return True
    927     return False
    928 
    929 def usage():
    930     print "\n\nUsage: sage-doctest [same options as sage -t] filenames"
    931     sys.exit(16)
    932        
    933 if __name__ ==  '__main__':
    934     import os, sys
    935     if len(argv) == 1:
    936         usage()
    937     else:
    938         if has_opt('help') or has_opt('h') or has_opt('?'):
    939             usage()
    940         optional   = has_opt('optional')
    941         long_time  = has_opt('long')
    942         verbose    = has_opt('verbose')
    943         do_timeit  = has_opt('timeit')
    944         gdb        = has_opt('gdb')
    945         memcheck   = has_opt('memcheck') or has_opt('valgrind')
    946         massif     = has_opt('massif')
    947         cachegrind = has_opt('cachegrind')
    948         omega      = has_opt('omega')
    949         force_lib  = has_opt('force_lib')
    950         random_order = parse_rand()
    951         only_optional, only_optional_tags = parse_only_opt()
    952         if long_time:
    953             TIMEOUT = TIMEOUT_LONG
    954         if gdb or memcheck or massif or cachegrind or omega:
    955             TIMEOUT = TIMEOUT_VALGRIND
    956         if argv[1][0] == '-':
    957             usage()
    958            
    959         ext = os.path.splitext(argv[1])[1]
    960 
    961         library_code = True
    962         dev_path = os.path.realpath(os.path.join(SAGE_ROOT, 'devel'))
    963         our_path = os.path.realpath(argv[1])
    964 
    965         if not force_lib and (ext in ['.spyx', '.sage'] or
    966                               not dev_path in our_path):
    967             library_code = False
    968 
    969         try:
    970             test_file(argv[1], library_code = library_code)
    971         except KeyboardInterrupt:
    972             sys.exit(1)
  • sage-eval

    diff --git a/sage-eval b/sage-eval
    a b  
    44from sage.all import *
    55from sage.calculus.predefined import x
    66from sage.misc.preparser import preparse
    7 from sage.misc.misc import tmp_filename
    87
    98if len(sys.argv) > 1:
    109    s = preparse(" ".join(sys.argv[1:]))
  • deleted file sage-maketest

    diff --git a/sage-maketest b/sage-maketest
    deleted file mode 100755
    + -  
    1 #!/usr/bin/env bash
    2 
    3 if [ -z "$SAGE_TESTDIR" -a -n "$SAGE_ROOT" ]; then
    4     SAGE_TESTDIR="$SAGE_ROOT"/tmp
    5 fi
    6 mkdir -p "$SAGE_TESTDIR"
    7 SAGE_TEST_LOG="$SAGE_TESTDIR/test.log"
    8 cd "$SAGE_TESTDIR"
    9 
    10 cat "$SAGE_ROOT/local/bin/sage-banner" > "$SAGE_TEST_LOG"
    11 echo `date` >> "$SAGE_TEST_LOG"
    12 
    13 # Run doctests on documentation and library, which end with a summary
    14 # report.  (We do this all in a single run of "sage -t" so we get a
    15 # single summary.)  We test doc/common and all subdirectories of doc/
    16 # whose names consist of two lowercase letters: those should match the
    17 # various languages.
    18 
    19 "$SAGE_ROOT"/sage -t --sagenb "$@" \
    20     "$SAGE_ROOT"/devel/sage/doc/common \
    21     "$SAGE_ROOT"/devel/sage/doc/[a-z][a-z] \
    22     "$SAGE_ROOT"/devel/sage/sage 2>&1 | tee -a "$SAGE_TEST_LOG"
    23 
    24 if [ "$SAGE_TESTDIR" = "$SAGE_ROOT/tmp" ]; then
    25     cat "$SAGE_TEST_LOG" >> "$SAGE_ROOT"/test.log
    26 fi
    27 
    28 echo "Please see $SAGE_TEST_LOG for the complete log from this test."
    29 
    30 exit 0
  • deleted file sage-ptest

    diff --git a/sage-ptest b/sage-ptest
    deleted file mode 100755
    + -  
    1 #!/usr/bin/env python
    2 
    3 # Usage: sage -tp N <options> <files>
    4 #
    5 # <options> may include
    6 #      --long                include lines with the phrase 'long time'
    7 #      --verbose             debugging output during the test
    8 #      --optional            also test all #optional examples
    9 #      --only-optional <tag1,...,tagn>    only run tests including one
    10 #                                         of the #optional tags
    11 #      --randorder[=seed]    randomize order of tests
    12 #
    13 # This runs doctests on <files> in parallel using N threads.  If N is
    14 # zero or omitted, set N to the value of the environment variable
    15 # SAGE_NUM_THREADS_PARALLEL, which is set in sage-env.
    16 
    17 import os
    18 import shutil
    19 import sys
    20 import time
    21 import pickle
    22 import signal
    23 import thread
    24 import tempfile
    25 import subprocess
    26 import multiprocessing
    27 import socket
    28 import stat
    29 import re
    30 
    31 def usage():
    32     print """Usage: sage -tp N <options> <files>
    33 
    34 <options> may include
    35      --long                include lines with the phrase 'long time'
    36      --verbose             debugging output during the test
    37      --optional            also test all #optional examples
    38      --only-optional <tag1,...,tagn>    only run tests including one
    39                                         of the #optional tags
    40      --randorder[=seed]    randomize order of tests
    41 
    42 This runs doctests on <files> in parallel using N threads.  If N is
    43 zero or omitted, try to use a sensible default number of threads: if
    44 the '-j' flag of the environment variable 'MAKE' or 'MAKEFLAGS' is set,
    45 use that setting. Otherwise, use min(8, number of CPU cores)."""
    46 
    47 if len(sys.argv) == 1:
    48     usage()
    49     exit(1)
    50 
    51 SAGE_ROOT = os.path.realpath(os.environ['SAGE_ROOT'])
    52 SAGE_SITE = os.path.realpath(os.path.join(os.environ['SAGE_LOCAL'],
    53                                           'lib', 'python', 'site-packages'))
    54 BUILD_DIR = os.path.realpath(os.path.join(SAGE_ROOT, 'devel', 'sage', 'build'))
    55 
    56 try:
    57     XML_RESULTS = os.environ['XML_RESULTS']
    58 except KeyError:
    59     XML_RESULTS = None
    60 
    61 numiteration = int(os.environ.get('SAGE_TEST_ITER', 1))
    62 numglobaliteration = int(os.environ.get('SAGE_TEST_GLOBAL_ITER', 1))
    63 print 'Global iterations: ' + str(numglobaliteration)
    64 print 'File iterations: ' + str(numiteration)
    65 
    66 # Exit status for the whole run.
    67 err = 0
    68 
    69 # Check that the current directory is sufficiently safe to run Python
    70 # code from.  We use the check added to Python for this, which gives a
    71 # warning when the current directory is considered unsafe.  We promote
    72 # this warning to an error with -Werror.  See sage/tests/cmdline.py
    73 # for a doctest that this works, see also Sage Trac #13579.
    74 import subprocess
    75 with open(os.devnull, 'w') as dev_null:
    76     if subprocess.call(['python', '-Werror', '-c', ''], stdout=dev_null, stderr=dev_null) != 0:
    77         raise RuntimeError("refusing to run doctests from the current "\
    78             "directory '{}' since untrusted users could put files in "\
    79             "this directory, making it unsafe to run Sage code from"\
    80             .format(os.getcwd()))
    81 
    82 def strip_automount_prefix(filename):
    83     """
    84     Strip prefixes added on automounted filesystems in some cases,
    85     which make the absolute path appear hidden.
    86 
    87     AUTHOR:
    88         -- Kate Minola
    89     """
    90     sep = os.path.sep
    91     str = filename.split(sep,2)
    92     if len(str) < 2:
    93         new = sep
    94     else:
    95         new = sep + str[1]
    96     if os.path.exists(new):
    97         inode1 = os.stat(filename)[1]
    98         inode2 = os.stat(new)[1]
    99         if inode1 == inode2:
    100             filename = new
    101     return filename
    102 
    103 def abspath(x):
    104     """
    105     This function returns the absolute path (adjusted for NFS)
    106     """
    107     return strip_automount_prefix(os.path.abspath(x))
    108 
    109 def abs_sage_path(f):
    110     """
    111     Return the absolute path, relative to the sage root or current directory
    112     """
    113     global CUR
    114 
    115     abs_path = abspath(f)
    116     if abs_path.startswith(SAGE_ROOT):
    117         abs_path = abs_path[len(SAGE_ROOT) + 1:]
    118     elif abs_path.startswith(CUR):
    119         abs_path = abs_path[len(CUR) + 1:]
    120 
    121     return abs_path
    122 
    123 def test_cmd(f):
    124     """
    125     Return the test command for the file
    126     """
    127     global opts
    128     return "sage -t %s %s" % (opts, abs_sage_path(f))
    129 
    130 def skip(F):
    131     """
    132     Returns true if the file should not be tested
    133     """
    134     if not os.path.exists(F):
    135         # XXX IMHO this should never happen; in case it does, it's certainly
    136         #     an error to be reported (either filesystem, or bad name specified
    137         #     on the command line). -leif
    138         return True
    139     G = abspath(F)
    140     i = G.rfind(os.path.sep)
    141     # XXX The following should IMHO be performed in populatefilelist():
    142     #     (Currently, populatefilelist() only looks for "__nodoctest__".)
    143     if os.path.exists(os.path.join(G[:i], 'nodoctest.py')):
    144         printmutex.acquire()
    145         print "%s (skipping) -- nodoctest.py file in directory" % test_cmd(F)
    146         sys.stdout.flush()
    147         printmutex.release()
    148         return True
    149     filenm = os.path.split(F)[1]
    150     if (filenm[0] == '.' or (os.path.sep + '.' in G.lstrip(os.path.sep + '.'))
    151         or 'nodoctest' in open(G).read()[:50]):
    152         return True
    153     if G.find(os.path.join('doc', 'output')) != -1:
    154         return True
    155     # XXX The following is (also/already) handled in populatefilelist():
    156     if not (os.path.splitext(F)[1] in ['.py', '.pyx', '.spyx', '.tex', '.pxi', '.sage', '.rst']):
    157         return True
    158     return False
    159 
    160 def test_file(F):
    161     """
    162     This is the function that actually tests a file
    163     """
    164     global opts
    165     outfile = tempfile.NamedTemporaryFile()
    166     base, ext = os.path.splitext(F)
    167 
    168     cmd = 'doctest ' + opts
    169     if SAGE_SITE in os.path.realpath(F) and not '-force_lib' in cmd:
    170         cmd += ' -force_lib'
    171 
    172     filestr = os.path.split(F)[1]
    173     for i in range(0,numiteration):
    174         os.chdir(os.path.dirname(F))
    175         command = os.path.join(SAGE_ROOT, 'local', 'bin', 'sage-%s' % cmd)
    176         # FIXME: Why call bash here? (Also, we use 'shell=True' below anyway.)
    177         s = 'bash -c "%s %s > %s" ' % (command, filestr, outfile.name)
    178         try:
    179             t = time.time()
    180             ret = subprocess.call(s, shell=True)
    181             finished_time = time.time() - t
    182         except:
    183             ol = outfile.read()
    184             return (F, 32, 0, ol)
    185         ol = outfile.read()
    186         if ret != 0:
    187             break
    188     return (F, ret, finished_time, ol)
    189 
    190 def process_result(result):
    191     """
    192     This file takes a tuple in the form
    193     (F, ret, finished_time, ol)
    194     and processes it to display/log the appropriate output.
    195     """
    196     global err, failed, time_dict
    197     F = result[0]
    198     ret = result[1]
    199     finished_time = result[2]
    200     ol = result[3]
    201     err = err | ret
    202     if ret != 0:
    203         if ret == 128:
    204             numfail = ol.count('Expected:') + ol.count('Expected nothing') + ol.count('Exception raised:')
    205             failed.append(test_cmd(F) + (" # %s doctests failed" % numfail))
    206             ret = numfail
    207         elif ret == 64:
    208             failed.append(test_cmd(F) + " # Time out")
    209         elif ret == 8:
    210             failed.append(test_cmd(F) + " # Exception from doctest framework")
    211         elif ret == 4:
    212             failed.append(test_cmd(F) + " # Killed/crashed")
    213         elif ret == 2:
    214             failed.append(test_cmd(F) + " # KeyboardInterrupt")
    215         elif ret == 1:
    216             failed.append(test_cmd(F) + " # File not found")
    217         else:
    218             failed.append(test_cmd(F))
    219 
    220     print test_cmd(F)
    221     sys.stdout.flush()
    222 
    223     if ol!="" and (not ol.isspace()):
    224         if (ol[len(ol)-1]=="\n"):
    225             ol=ol[0:len(ol)-1]
    226         print ol
    227         sys.stdout.flush()
    228     time_dict[abs_sage_path(F)] = finished_time
    229     if XML_RESULTS:
    230         t = finished_time
    231         failures = int(ret == 128)
    232         errors = int(ret and not failures)
    233         path = F.split(os.path.sep)
    234         while 'sage' in path:
    235             path = path[path.index('sage')+1:]
    236         path[-1] = os.path.splitext(path[-1])[0]
    237         module = '.'.join(path)
    238         if (failures or errors) and '#' in failed[-1]:
    239             type = "error" if errors else "failure"
    240             cause = failed[-1].split('#')[-1].strip()
    241             failure_item = "<%s type='%s'>%s</%s>" % (type, cause, cause, type)
    242         else:
    243             failure_item = ""
    244         f = open(os.path.join(XML_RESULTS, module + '.xml'), 'w')
    245         f.write("""
    246             <?xml version="1.0" ?>
    247             <testsuite name="%(module)s" errors="%(errors)s" failures="%(failures)s" tests="1" time="%(t)s">
    248             <testcase classname="%(module)s" name="test">
    249             %(failure_item)s
    250             </testcase>
    251             </testsuite>
    252         """.strip() % locals())
    253         f.close()
    254     print "\t [%.1f s]"%(finished_time)
    255     sys.stdout.flush()
    256 
    257 def infiles_cmp(a,b):
    258     """
    259     This compare function is used to sort the list of filenames by the time they take to run
    260     """
    261     global time_dict
    262     if time_dict.has_key(abs_sage_path(a)):
    263         if time_dict.has_key(abs_sage_path(b)):
    264             return cmp(time_dict[abs_sage_path(a)],time_dict[abs_sage_path(b)])
    265         else:
    266             return 1
    267     else:
    268         return -1
    269 
    270 def populatefilelist(filelist):
    271     """
    272     This populates the file list by expanding directories into lists of files
    273     """
    274     global CUR
    275     filemutex.acquire()
    276     for FF in filelist:
    277         if os.path.isfile(FF):
    278             if skip(FF):
    279                 continue
    280             if not os.path.isabs(FF):
    281                 cwd = os.getcwd()
    282                 files.append(os.path.join(cwd, FF))
    283             else:
    284                 files.append(FF)
    285             continue
    286 
    287         curdir = os.getcwd()
    288         walkdir = os.path.join(CUR,FF)
    289 
    290         for root, dirs, lfiles in os.walk(walkdir):
    291             for F in lfiles:
    292                 base, ext = os.path.splitext(F)
    293                 if not (ext in ['.sage', '.py', '.pyx', '.spyx', '.tex', '.pxi', '.rst']):
    294                     continue
    295                 elif '__nodoctest__' in files:
    296                     # XXX Shouldn't this be 'lfiles'?
    297                     # Also, this test should IMHO be in the outer loop (1 level).
    298                     # Furthermore, the current practice is to put "nodoctest.py"
    299                     # files in the directories that should be skipped, not
    300                     # "__nodoctest__". (I haven't found a single instance of the
    301                     # latter in Sage 4.6.1.alpha3.)
    302                     # "nodoctest.py" is handled in skip() (!), to also be fixed.
    303                     # -leif
    304                     continue
    305                 appendstr = os.path.join(root,F)
    306                 if skip(appendstr):
    307                     continue
    308                 if os.path.realpath(appendstr).startswith(BUILD_DIR):
    309                     continue
    310                 files.append(appendstr)
    311             for D in dirs:
    312                 if '#' in D or (os.path.sep + 'notes' in D):
    313                     dirs.remove(D)
    314     filemutex.release()
    315     return 0
    316 
    317 for gr in range(0,numglobaliteration):
    318     argv = sys.argv
    319     opts = ' '.join([X for X in argv if X[0] == '-'])
    320     argv = [X for X in argv if X[0] != '-']
    321 
    322     try:
    323         numthreads = int(argv[1])
    324         infiles = argv[2:]
    325     except ValueError:
    326         # can't convert first arg to an integer: arg was probably omitted
    327         numthreads = 0
    328         infiles = argv[1:]
    329 
    330     if '-sagenb' in opts:
    331         opts = opts.replace('--sagenb', '').replace('-sagenb', '')
    332 
    333         # Find SageNB's home.
    334         from pkg_resources import Requirement, working_set
    335         sagenb_loc = working_set.find(Requirement.parse('sagenb')).location
    336 
    337         # In case we're using setuptools' "develop" mode.
    338         if not SAGE_SITE in sagenb_loc:
    339             opts += ' -force_lib'
    340 
    341         infiles.append(os.path.join(sagenb_loc, 'sagenb'))
    342 
    343     verbose = ('-verbose' in opts or '--verbose' in opts)
    344 
    345     if numthreads == 0:
    346         try:
    347             numthreads = int(os.environ['SAGE_NUM_THREADS_PARALLEL'])
    348         except KeyError:
    349             numthreads = 1
    350 
    351     if numthreads < 1 or len(infiles) == 0:
    352         if numthreads < 1:
    353             print "Usage: sage -tp <numthreads> <files or directories>: <numthreads> must be non-negative."
    354         else:
    355             print "Usage: sage -tp <numthreads> <files or directories>: no files or directories specified."
    356         print "For more information, type 'sage --advanced'."
    357         sys.exit(1)
    358 
    359     infiles.sort()
    360 
    361     files = list()
    362 
    363     t0 = time.time()
    364     filemutex = thread.allocate_lock()
    365     printmutex = thread.allocate_lock()
    366     SAGE_TESTDIR = os.environ['SAGE_TESTDIR']
    367     #Pick a filename for the timing files -- long vs normal
    368     # TODO: perhaps these files shouldn't be hidden?  Also, don't
    369     # store them in SAGE_TESTDIR, in case the user wants to test in
    370     # some temporary directory: store them somewhere more permanent.
    371     if opts.count("-long"):
    372         time_file_name = os.path.join(SAGE_TESTDIR,
    373                                       '.ptest_timing_long')
    374     else:
    375         time_file_name = os.path.join(SAGE_TESTDIR,
    376                                       '.ptest_timing')
    377     time_dict = { }
    378     try:
    379         with open(time_file_name) as time_file:
    380             time_dict = pickle.load(time_file)
    381         if opts.count("-long"):
    382             print "Using long cached timings to run longest doctests first."
    383         else:
    384             print "Using cached timings to run longest doctests first."
    385         from copy import copy
    386         time_dict_old = copy(time_dict)
    387     except:
    388         time_dict = { }
    389         if opts.count("-long"):
    390             print "No long cached timings exist; will create for successful files."
    391         else:
    392             print "No cached timings exist; will create for successful files."
    393         time_dict_old = None
    394     done = False
    395 
    396     CUR = abspath(os.getcwd())
    397 
    398     failed = []
    399 
    400     HOSTNAME = socket.gethostname().replace('-','_').replace('/','_').replace('\\','_')
    401     # Should TMP be a subdirectory of tempfile.gettempdir() rather than SAGE_TESTDIR?
    402     TMP = os.path.join(SAGE_TESTDIR, '%s-%s' % (HOSTNAME, os.getpid()))
    403     TMP = os.path.abspath(TMP)
    404     try:
    405         os.makedirs(TMP)
    406     except OSError:
    407         # If TMP already exists, remove it and re-create it
    408         if os.path.isdir(TMP):
    409             shutil.rmtree(TMP)
    410             os.makedirs(TMP)
    411         else:
    412             raise
    413 
    414     # Add rwx permissions for user to TMP:
    415     os.chmod(TMP, os.stat(TMP)[0] | stat.S_IRWXU)
    416     os.environ['SAGE_TESTDIR'] = TMP
    417     if verbose:
    418         print
    419         print "Using the directory"
    420         print "   '%s'." % TMP
    421         print "for doctesting.  If all doctests pass, this directory will"
    422         print "be deleted automatically."
    423         print
    424 
    425     populatefilelist(infiles)
    426     #Sort the files by test time
    427     files.sort(infiles_cmp)
    428     files.reverse()
    429     interrupt = False
    430 
    431     numthreads = min(numthreads, len(files))  # don't use more threads than files
    432        
    433     if len(files) == 1:
    434         file_str = "1 file"
    435     else:
    436         file_str = "%i files" % len(files)
    437     if numthreads == 1:
    438         jobs_str = "using 1 thread"  # not in parallel if numthreads is 1
    439     else:
    440         jobs_str = "doing %s jobs in parallel" % numthreads
    441 
    442     print "Doctesting %s %s" % (file_str, jobs_str)
    443        
    444     try:
    445         p = multiprocessing.Pool(numthreads)
    446         for r in p.imap_unordered(test_file, files):
    447             #The format is  (F, ret, finished_time, ol)
    448             process_result(r)
    449     except KeyboardInterrupt:
    450         err = err | 2
    451         interrupt = True
    452         pass
    453     print " "
    454     print "-"*int(70)
    455 
    456     os.chdir(CUR)
    457 
    458     if verbose:
    459         print
    460         print "Removing the directory '%s'." % TMP
    461     try:
    462         os.rmdir(TMP)
    463         if verbose:
    464             print
    465             print "-"*int(70)
    466     except OSError:
    467         # TODO (probably in sage-doctest): if tests were interrupted
    468         # but there were no failures in the interrupted files, delete
    469         # the temporary files, so that this directory is empty.
    470         print "The temporary doctesting directory"
    471         print "   %s" % TMP
    472         print "was not removed: it is not empty, presumably because doctests"
    473         print "failed or doctesting was interrupted."
    474         print
    475         print "-"*int(70)
    476 
    477     if len(failed) == 0:
    478         if interrupt == False:
    479             print "All tests passed!"
    480         else:
    481             print "Keyboard Interrupt: All tests that ran passed."
    482     else:
    483         if interrupt:
    484             print "Keyboard Interrupt, not all tests ran"
    485         elif opts=="-long" or len(opts)==0:
    486             time_dict_ran = time_dict
    487             time_dict = { }
    488             failed_files = { }
    489             if opts=="-long":
    490                 for F in failed:
    491                     failed_files[F.split('#')[0].split()[3]] = None
    492             else:
    493                 for F in failed:
    494                     failed_files[F.split('#')[0].split()[2]] = None
    495             for F in time_dict_ran:
    496                 if F not in failed_files:
    497                     time_dict[F] = time_dict_ran[F]
    498             if time_dict_old is not None:
    499                 for F in time_dict_old:
    500                     if F not in time_dict:
    501                         time_dict[F] = time_dict_old[F]
    502         print "\nThe following tests failed:\n"
    503         for i in range(len(failed)):
    504                print "\t", failed[i]
    505         print "-"*int(70)
    506 
    507     #Only update timings if we are doing something standard
    508     opts = opts.strip()
    509     if (opts=="-long" or len(opts)==0) and not interrupt:
    510         with open(time_file_name,"w") as time_file:
    511             pickle.dump(time_dict, time_file)
    512             print "Timings have been updated."
    513 
    514     print "Total time for all tests: %.1f seconds"%(time.time() - t0)
    515 
    516 sys.exit(err)
  • new file sage-runtests

    diff --git a/sage-runtests b/sage-runtests
    new file mode 100755
    - +  
     1#!/usr/bin/env python
     2
     3import optparse, os, sys
     4
     5if __name__ == "__main__":
     6    parser = optparse.OptionParser()
     7
     8    def nthreads_callback(option, opt_str, value, parser):
     9        assert value is None
     10        if parser.rargs: # there are more arguments
     11            try:
     12                next_arg = int(parser.rargs[0])
     13                parser.rargs.pop(0)
     14            except ValueError:
     15                # No explicit number of threads passed
     16                next_arg = 0
     17        else:
     18            next_arg = 0
     19        parser.values.nthreads = next_arg
     20
     21    parser.add_option("-p", "--nthreads", action="callback", callback=nthreads_callback, nargs=0, metavar="N", help="tests in parallel using N threads with 0 interpreted as minimum(8, cpu_count())")
     22    parser.add_option("--serial", action="store_true", default=False, help="run tests in a single process in series")
     23    parser.add_option("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file")
     24    parser.add_option("-a", "--all", action="store_true", default=False, help="test all files in the Sage library")
     25    parser.add_option("--logfile", metavar="FILE", help="log all output to FILE")
     26    parser.add_option("--sagenb", action="store_true", default=False, help="test all sagenb files")
     27
     28    parser.add_option("--long", action="store_true", default=False, help="include lines with the phrase 'long time'")
     29    parser.add_option("--optional", metavar="OPTIONAL_PKGS", default="sage", \
     30                          help="only run tests including one of the #optional tags listed in OPTIONAL_PKGS; if 'sage' is listed will also test the standard doctests; if OPTIONAL_PKGS='all' all tests will be run")
     31    parser.add_option("--randorder", type=int, metavar="SEED", help="randomize order of tests")
     32    parser.add_option("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times")
     33    parser.add_option("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure")
     34
     35    parser.add_option("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file")
     36    parser.add_option("--force_lib", "--force-lib", action="store_true", default=False, help="assume all files are Sage library files, regardless of location, ie don't import anything from the file tested")
     37    parser.add_option("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths")
     38    parser.add_option("--verbose", action="store_true", default=False, help="print debugging output during the test")
     39    parser.add_option("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised")
     40
     41    parser.add_option("--gdb", action="store_true", default=False, help="run doctests under the control of gdb")
     42    parser.add_option("--valgrind", "--memcheck", action="store_true", default=False,
     43                      help="run doctests using Valgrind's memcheck tool.  The log " + \
     44                         "files are named sage-memcheck.PID and can be found in " + \
     45                         os.path.join(os.environ["DOT_SAGE"], "valgrind"))
     46    parser.add_option("--massif", action="store_true", default=False,
     47                      help="run doctests using Valgrind's massif tool.  The log " + \
     48                         "files are named sage-massif.PID and can be found in " + \
     49                         os.path.join(os.environ["DOT_SAGE"], "valgrind"))
     50    parser.add_option("--cachegrind", action="store_true", default=False,
     51                      help="run doctests using Valgrind's cachegrind tool.  The log " + \
     52                         "files are named sage-cachegrind.PID and can be found in " + \
     53                         os.path.join(os.environ["DOT_SAGE"], "valgrind"))
     54    parser.add_option("--omega", action="store_true", default=False,
     55                      help="run doctests using Valgrind's omega tool.  The log " + \
     56                         "files are named sage-omega.PID and can be found in " + \
     57                         os.path.join(os.environ["DOT_SAGE"], "valgrind"))
     58
     59    parser.add_option("-f", "--failed", action="store_true", default=False, \
     60                          help="doctest only those files that failed in the previous run")
     61    parser.add_option("--new", action="store_true", default=False, help="doctest only those files that have been changed in the repository and not yet been committed")
     62
     63    parser.add_option("--stats_path", "--stats-path", default=os.path.join(os.path.expanduser("~/.sage/timings2.json")), \
     64                          help="path to a json dictionary for the latest run storing a timing for each file")
     65
     66    parser.set_usage("sage -t [options] filenames")
     67    from sage.doctest.control import DocTestController
     68    options, args = parser.parse_args()
     69    if len(args) == 0 and not (options.all or options.sagenb or options.new):
     70        parser.print_help()
     71        sys.exit(8)
     72    else:
     73        DC = DocTestController(*parser.parse_args())
     74        err = DC.run()
     75
     76        # We use os._exit rather then sys.exit since sys.exit wasn't
     77        # completely quitting on sage.math after a KeyboardInterrupt
     78        os._exit(err)
  • deleted file sage-test

    diff --git a/sage-test b/sage-test
    deleted file mode 100755
    + -  
    1 #!/usr/bin/env python
    2 
    3 ####################################################################
    4 # Run the Sage doctest system on the collection of files and
    5 # directories specified on the command line.
    6 
    7 ####################################################################
    8 
    9 import os, signal, sys, time
    10 
    11 
    12 argv = sys.argv
    13 
    14 opts = ' '.join([X for X in argv if X[0] == '-'])
    15 argv = [X for X in argv if X[0] != '-']
    16 
    17 t0 = time.time()
    18 
    19 from os.path import abspath
    20 
    21 SAGE_ROOT = os.environ['SAGE_ROOT']
    22 SAGE_SITE = os.path.realpath(os.path.join(os.environ['SAGE_LOCAL'],
    23                                           'lib', 'python', 'site-packages'))
    24 
    25 try:
    26     XML_RESULTS = os.environ['XML_RESULTS']
    27 except KeyError:
    28     XML_RESULTS = None
    29 
    30 
    31 # TODO: should we set SAGE_TESTDIR to a temporary directory like
    32 # tempfile.gettempdir()?
    33 if 'SAGE_TESTDIR' not in os.environ:
    34     os.environ['SAGE_TESTDIR'] = os.path.join(SAGE_ROOT, "tmp")
    35 SAGE_TESTDIR = os.environ['SAGE_TESTDIR']
    36 try:
    37     os.makedirs(SAGE_TESTDIR)
    38 except OSError:
    39     if not os.path.isdir(SAGE_TESTDIR):
    40         raise
    41 
    42 # Check that the current directory is sufficiently safe to run Python
    43 # code from.  We use the check added to Python for this, which gives a
    44 # warning when the current directory is considered unsafe.  We promote
    45 # this warning to an error with -Werror.  See sage/tests/cmdline.py
    46 # for a doctest that this works, see also Sage Trac #13579.
    47 import subprocess
    48 with open(os.devnull, 'w') as dev_null:
    49     if subprocess.call(['python', '-Werror', '-c', ''], stdout=dev_null, stderr=dev_null) != 0:
    50         raise RuntimeError("refusing to run doctests from the current "\
    51             "directory '{}' since untrusted users could put files in "\
    52             "this directory, making it unsafe to run Sage code from"\
    53             .format(os.getcwd()))
    54 
    55 def sage_test_command(f):
    56     g = abspath(f)
    57     if SAGE_ROOT in g:
    58         f = g[g.find(SAGE_ROOT)+len(SAGE_ROOT)+1:]
    59     return 'sage -t %s "%s"'%(opts, f)
    60 
    61 def skip(F):
    62     G = abspath(F)
    63     i = G.rfind(os.sep)
    64     if os.path.exists(os.path.join(G[:i], 'nodoctest.py')):
    65         print "%s (skipping) -- nodoctest.py file in directory"%sage_test_command(F)
    66         return True
    67 
    68     if 'nodoctest' in open(G).read()[:50]:
    69         return True
    70     if G.find(os.path.join('doc', 'output')) != -1:
    71         return True
    72 
    73     sys.stdout.write("%-60s"%sage_test_command(F)+"\n")
    74     sys.stdout.flush()
    75     return False
    76 
    77 failed = []
    78 
    79 def test(F, cmd):
    80     from subprocess import call
    81     t = time.time()
    82     if skip(F):
    83         return 0
    84     s = os.path.join(SAGE_ROOT, 'local', 'bin', 'sage-%s' % cmd) + ' "%s"' % F
    85     err = call(s, shell=True)
    86 
    87     # Check the process exit code that sage-doctest returns
    88 
    89     if err == 1: # process exit code 1: File not found
    90         failed.append(sage_test_command(F)+" # File not found")
    91     elif err == 2: # process exit code 2: KeyboardInterrupt
    92         failed.append(sage_test_command(F)+" # KeyboardInterrupt")
    93         raise KeyboardInterrupt
    94     elif err == 4: # process exit code 4: Terminated by signal
    95         failed.append(sage_test_command(F)+" # Killed/crashed")
    96     elif err == 8: # process exit code 8: Unhandled doctest exception
    97         failed.append(sage_test_command(F)+" # Exception from doctest framework")
    98     elif err == 64: # process exit code 64: Time out
    99         failed.append(sage_test_command(F)+" # Time out")
    100     elif err == 128: # process exit code 128: Regular doctest failures
    101         failed.append(sage_test_command(F))
    102     elif err != 0:
    103         failed.append(sage_test_command(F))
    104     t = time.time() - t
    105     print "\t [%.1f s]" % t
    106     if XML_RESULTS:
    107         failures = int(err == 128)
    108         errors = int(err and not failures)
    109         path = F.split(os.path.sep)
    110         while 'sage' in path:
    111             path = path[path.index('sage')+1:]
    112         path[-1] = os.path.splitext(path[-1])[0]
    113         module = '.'.join(path)
    114         if errors and '#' in failed[-1]:
    115             cause = failed[-1].split('#')[-1].strip()
    116             failure_item = "<error type='%s'>%s</error>" % (cause, cause)
    117         else:
    118             failure_item = ""
    119         f = open(os.path.join(XML_RESULTS, module + '.xml'), 'w')
    120         f.write("""
    121             <?xml version="1.0" ?>
    122             <testsuite name="%(module)s" errors="%(errors)s" failures="%(failures)s" tests="1" time="%(t)s">
    123             <testcase classname="%(module)s" name="test">
    124             %(failure_item)s
    125             </testcase>
    126             </testsuite>
    127         """.strip() % locals())
    128         f.close()
    129     return err
    130 
    131 
    132 def test_file(F):
    133     if not os.path.exists(F):
    134         if os.path.exists(os.path.join(SAGE_ROOT, F)):
    135             F = os.path.join(SAGE_ROOT, F)
    136     if not os.path.exists(F):
    137         if F[:6] != "__test" and not F.endswith('.png'):
    138             print "ERROR: File %s is missing" % os.path.join(os.curdir, F)
    139             failed.append(os.path.join(os.curdir, F) + " # File not found")
    140         return 1
    141 
    142     extra_opts = ''
    143     if SAGE_SITE in os.path.realpath(F) and not '-force_lib' in opts:
    144         extra_opts = ' -force_lib'
    145 
    146     base, ext = os.path.splitext(F)
    147     err = 0
    148     if ext in ['.py', '.spyx', '.pyx', '.tex', '.pxi', '.sage', '.rst']:
    149         err = err | test(F, 'doctest ' + opts + extra_opts)
    150     elif (os.path.isdir(F) and  not '#' in F and
    151           not os.sep + 'notes' in F):
    152         ld = os.listdir(F)
    153         if not ('__nodoctest__' in ld):
    154             for L in ld:
    155                 err = err | test_file(os.path.join(F, L))
    156     return err
    157 
    158 files = argv[1:]
    159 
    160 if '-sagenb' in opts:
    161     opts = opts.replace('--sagenb', '').replace('-sagenb', '')
    162 
    163     # Find SageNB's home.
    164     from pkg_resources import Requirement, working_set
    165     sagenb_loc = working_set.find(Requirement.parse('sagenb')).location
    166 
    167     # In case we're using setuptools' "develop" mode.
    168     if not SAGE_SITE in sagenb_loc:
    169         opts += ' -force_lib'
    170 
    171     files.append(os.path.join(sagenb_loc, 'sagenb'))
    172 
    173 if len(files) == 0:
    174     print "Usage: sage -t [-verbose] [-long] [-optional] [-only-optional=list,of,tags] <files or directories>"
    175     print "Test the docstrings in each listed Python or Cython file, and "
    176     print "if a directory is given, test the docstrings in all files in"
    177     print "all subdirectories of that directory."
    178     print ""
    179     print "OPTIONS:"
    180     print "     -force_lib     -- assume all files are Sage library files,"
    181     print "                       regardless of location"
    182     print "     -long          -- include lines with the phrase 'long time'"
    183     print "     -verbose       -- print debugging output during the test"
    184     print "     -optional      -- also test all #optional examples"
    185     print "     -only-optional=list,of,tags -- only run doctests with "
    186     print "                       #optional <nonempty subset of tags>"
    187     print "      if the list of tags is omitted, run all optional doctests"
    188     print "     -randorder     -- if given, randomize *order* of tests"
    189     print "     -randorder=seed-- use seed to get same random order"
    190     print "     -sagenb        -- test all sagenb files"
    191 
    192     sys.exit(1)
    193 
    194 files.sort()
    195 
    196 err = 0
    197 for F in files:
    198     try:
    199         err = err | test_file(F)
    200     except KeyboardInterrupt:
    201         print "Aborting further tests."
    202         err = err | 2
    203         break
    204 
    205 print " "
    206 print "-"*int(70)
    207 
    208 if len(failed) == 0:
    209     print "All tests passed!"
    210 else:
    211     print "The following tests failed:\n"
    212     print "\n\t" + "\n\t".join(failed)
    213 
    214 print "Total time for all tests: %.1f seconds"%(time.time() - t0)
    215 sys.exit(err)
  • deleted file sage-test-new

    diff --git a/sage-test-new b/sage-test-new
    deleted file mode 100755
    + -  
    1 #!/usr/bin/env python
    2 
    3 import os
    4 
    5 print "Testing all files that have changed in the repository"
    6 print "that have not yet been commited using 'hg_sage.commit()'."
    7 
    8 os.chdir('%s/devel/sage'%os.environ['SAGE_ROOT'])
    9 
    10 v = os.popen('hg status').read()
    11 print v
    12 F = []
    13 for X in v.split('\n'):
    14     try:
    15         c, filename = X.split()
    16     except:
    17         break
    18     if c == 'M':
    19         F.append(filename)
    20 
    21 if len(F) == 0:
    22     print "No files to test."
    23 else:
    24     cmd = 'sage -t %s'%(' '.join([str(x) for x in F]))
    25     print cmd
    26     os.system(cmd)
    27