Ticket #7514: sagelib-7514_combined.2.patch

File sagelib-7514_combined.2.patch, 32.4 KB (added by mpatel, 11 years ago)

Fixed doctest failures. Depends on Sage 4.3, #7483, #7482. Replaces previous.

  • sage/misc/all.py

    # HG changeset patch
    # User William Stein <wstein@gmail.com>
    # Date 1258877567 28800
    # Node ID e08039b9dd24e2372e4ad21303f54b8f13aa7e1f
    # Parent  0ce03d21b776d92c73bf901c647b6be4a921c697
    trac 7514 -- rewrite load and attach
    
    diff --git a/sage/misc/all.py b/sage/misc/all.py
    a b from flatten import flatten 
    2121
    2222from map_threaded import map_threaded
    2323
    24 from session import load_session, save_session, show_identifiers
     24from session import load_session, save_session, show_identifiers, attach
    2525
    2626from remote_file import get_remote_file
    2727
    28 from attach import attach
    29 
    3028from profiler import Profiler
    3129
    3230from mrange import xmrange, mrange, xmrange_iter, mrange_iter, cartesian_product_iterator
    from mathml import mathml 
    6159
    6260from defaults import set_default_variable_name
    6361
    64 from preparser import preparse, implicit_multiplication, BackslashOperator
     62from preparser import preparse, implicit_multiplication, BackslashOperator, attached_files, detach
    6563
    6664from interpreter import preparser
    6765
  • sage/misc/attach.py

    diff --git a/sage/misc/attach.py b/sage/misc/attach.py
    a b Attach a file to a running instance of S 
    33"""
    44
    55class Attach:
    6     r"""
     6    """
    77    Attach a file to a running instance of Sage.
    8    
    9     .. note::
    10 
    11        ``attach`` is *not* a function and is not part of the Python
    12        language.
    13    
    14     ``load`` is exactly the same as attach, but doesn't
    15     automatically reload a file when it changes.
    16    
    17     You attach a file, e.g., ``foo.sage`` or
    18     ``foo.py`` or ``foo.spyx``, to a running
    19     Sage session by typing
    20    
    21     ::
    22    
    23         sage: attach foo.sage   # or foo.py   or foo.spyx  (not tested)
    24    
    25     The contents of the file are then loaded, which means they are
    26     read into the running Sage session. For example, if ``foo.sage``
    27     contains ``x=5``, after attaching ``foo.sage`` the variable ``x``
    28     will be set to 5. Moreover, any time you change ``foo.sage``, the
    29     attached file will be re-read automatically (with no intervention
    30     on your part).
    31    
    32     USAGE: ``attach file1 file2 ...`` - space-separated
    33     list of .py, .spyx, and .sage files.
    34    
    35     EFFECT: Each file is read in and added to an internal list of
    36     watched files. The meaning of reading a file in depends on the file
    37     type:
    38    
    39    
    40     -  read in with no preparsing (so, e.g., ``23`` is 2
    41        bit-xor 3),
    42    
    43     -  preparsed then the result is read in
    44    
    45     -  *not* preparsed. Compiled to a module ``m`` then
    46        ``from m import *`` is executed.
    47    
    48    
    49     Type ``attached_files()`` for a list of all currently
    50     attached files. You can remove files from this list to stop them
    51     from being watched.
    52    
    53     .. note::
    54 
    55         ``attach`` is exactly the same as load, except it keeps track of
    56         the loaded file and automatically reloads it when it changes.
    578    """
    589    def __repr__(self):
    5910        return self.__doc__
  • sage/misc/interpreter.py

    diff --git a/sage/misc/interpreter.py b/sage/misc/interpreter.py
    a b import remote_file 
    105105
    106106from IPython.iplib import InteractiveShell
    107107
    108 import preparser_ipython 
    109 from preparser import preparse_file
     108import preparser_ipython
     109from preparser import preparse_file, load_wrap, modified_attached_files, attached_files
    110110
    111111import cython
    112112
    113 #import signal
    114 #def sig(x,n):
    115 #    raise KeyboardInterrupt
    116 #def unsetsig():
    117 #    signal.signal(signal.SIGINT, sig)
    118 
    119113# IPython has a prefilter() function that analyzes each input line. We redefine
    120114# it here to first pre-process certain forms of input
    121115
    import cython 
    127121
    128122
    129123
    130 attached = { }
    131 
    132 def attached_files():
    133     """
    134     Return a list of all files attached to the current session.
    135     """
    136     global attached
    137     X = attached.keys()
    138     X.sort()
    139     return X
    140 
    141124def load_startup_file(file):
    142125    if os.path.exists(file):
    143126        X = do_prefilter_paste('load "%s"'%file,False)
    def do_prefilter_paste(line, continuatio 
    152135    Alternate prefilter for input.
    153136
    154137    INPUT:
    155         line -- a single line; must *not* have any newlines in it
    156         continuation -- whether the input line is really part
    157                      of the previous line, because of open parens or backslash.
     138   
     139        - ``line`` -- a single line; must *not* have any newlines in it
     140        - ``continuation`` -- whether the input line is really part
     141          of the previous line, because of open parens or backslash.
    158142    """
    159143    if '\n' in line:
    160144        raise RuntimeError, "bug in function that calls do_prefilter_paste -- there can be no newlines in the input"
    161    
    162     global attached
    163145
    164146    # This is so it's OK to have lots of blank space at the
    165147    # beginning of any non-continuation line.
    def do_prefilter_paste(line, continuatio 
    174156       
    175157    line = line.rstrip()
    176158
    177     if not line.startswith('attach ') and not line.startswith('load ') and not line.startswith('%run '):
    178         for F in attached.keys():
    179             tm = attached[F]
    180             if os.path.exists(F) and os.path.getmtime(F) > tm:
    181                 # Reload F.
    182                 try:
    183                     if F.endswith('.py'):
    184                         _ip.runlines('%%run -i "%s"'%F)
    185                     elif F.endswith('.sage'):
    186                         _ip.runlines('%%run -i "%s"'%preparse_file_named(F))
    187                     elif F.endswith('.spyx') or F.endswith('.pyx'):
    188                         X = load_cython(F)
    189                         __IPYTHON__.push(X)
    190                     else:
    191                         line = 'load("%s")'%F
    192                     t = os.path.getmtime(F)                   
    193                     attached[F] = t
    194                 except IOError:
    195                     del attached[F]
    196                
    197 
    198     # Get rid of leading sage: so that pasting of examples from the documentation
    199     # works.  This is like MAGMA's SetLinePrompt(false).
     159    # Process attached files.
     160    for F in modified_attached_files():
     161        _ip.runlines(load_wrap(F))
     162       
     163    # Get rid of leading sage: prompts so that pasting of examples
     164    # from the documentation works.  This is like MAGMA's
     165    # SetLinePrompt(false).
    200166    for prompt in ['sage:', '>>>']:
    201167        if not continuation:
    202168            while True:
    def do_prefilter_paste(line, continuatio 
    216182    if line.lower() in ['quit', 'exit', 'quit;', 'exit;']:
    217183        line = '%quit'
    218184
    219     #################################################################
    220185    # An interactive load command, like iload in MAGMA.
    221     #################################################################
    222186    if line[:6] == 'iload ':
    223187        try:
    224188            name = str(eval(line[6:]))
    def do_prefilter_paste(line, continuatio 
    252216
    253217       
    254218    #################################################################
    255     # A "load" command, like \r file in PARI or load "file" in MAGMA
     219    # load and attach commands
    256220    #################################################################
    257     if line[:5] == 'load ':
    258         # The -i so the file is run with the same environment,
    259         # e.g., including the "from sage import *"
    260         try:
    261             name = str(eval(line[5:])).strip()
    262         except:
    263             name = str(line[5:].strip())
    264         if name.lower().startswith('http://'):
    265             name = remote_file.get_remote_file(name)
    266         if isinstance(name, str):
    267             if not os.path.exists(name):
    268                 raise ImportError, "File '%s' not found (be sure to give .sage, .py, or .pyx extension)"%name
    269             elif name.endswith('.py'):
    270                 try:
    271                     line = '%run -i "' + name + '"'
    272                 except IOError, s:
    273                     print s
    274                     raise ImportError, "Error loading '%s'"%name
    275             elif name.endswith('.sage'):
    276                 try:
    277                     line = '%run -i "' + preparse_file_named(name) + '"'
    278                 except IOError, s:
    279                     print s
    280                     raise ImportError, "Error loading '%s'"%name
    281                     line = ""
    282             elif name.endswith('.spyx') or name.endswith('.pyx'):
    283                 line = load_cython(name)
    284             else:
    285                 line = 'load("%s")'%name
    286                 #raise ImportError, "Loading of '%s' not implemented (load .py, .pyx, and .sage files)"%name
    287                 #line = ''
     221    for cmd in ['load', 'attach']:
     222        if line.lstrip().startswith(cmd+' '):
     223            j = line.find(cmd+' ')
     224            s = line[j+len(cmd)+1:].strip()
     225            if not s.startswith('('):
     226                line = ' '*j + load_wrap(s, cmd=='attach')
    288227
    289     # This is an attach command like in MAGMA.  The file to attach is
    290     # any file that could be loaded.  At attach time it is loaded as
    291     # above.  It is put in a list that is a variable with scope this
    292     # interpreter.py file.  Each time prefilter_paste is called and
    293     # continuation is False, check all attached file names and if any
    294     # have changed, compile the file, then use %run to load them again
    295     # as above.  This is like the MAGMA attach, but in some ways
    296     # better.  It is very nice for interactive code development.
    297    
    298     if line.startswith('attach '):
    299         # The -i so the file is run with the same environment,
    300         # e.g., including the "from sage import *"
    301         try:
    302             name = str(eval(line[7:]))
    303         except:
    304             name = str(line[7:].strip())
    305         name = os.path.abspath(name)
    306         if not os.path.exists(name):
    307             raise ImportError, "File '%s' not found  (be sure to give .sage, .py, or .pyx extension)."%name
    308         elif name.endswith('.py'):
    309             try:
    310                 line = '%run -i "' + name + '"'
    311                 attached[name] = os.path.getmtime(name)
    312             except IOError, OSError:
    313                 raise ImportError, "File '%s' not found."%name
    314         elif name.endswith('.sage'):
    315             try:
    316                 line = '%run -i "' + preparse_file_named(name) + '"'
    317                 attached[name] = os.path.getmtime(name)
    318             except IOError, OSError:
    319                 raise ImportError, "File '%s' not found."%name
    320         elif name.endswith('.pyx') or name.endswith('.spyx'):
    321             try:
    322                 line = load_cython(name)
    323                 attached[name] = os.path.getmtime(name)
    324             except IOError, OSError:
    325                 raise ImportError, "File '%s' not found."%name
    326                 line = ''
    327         else:
    328             #line = 'load("%s")'%name
    329             raise ImportError, "Attaching of '%s' not implemented (load .py, .pyx, and .sage files)"%name
    330        
    331228    if len(line) > 0:
    332229        line = preparser_ipython.preparse_ipython(line, not continuation)
     230
    333231    return line
    334232
    335233def load_cython(name):
    def preparse_file_named_to_stream(name,  
    434332    os.chdir(dir)
    435333    contents = open(name).read()
    436334    contents = handle_encoding_declaration(contents, out)
    437     parsed = preparse_file(contents, attached, do_time=True)
     335    parsed = preparse_file(contents)
    438336    os.chdir(cur)
    439337    out.write("# -*- encoding: utf-8 -*-\n")
    440338    out.write('#'*70+'\n')
    def sage_prompt(): 
    568466__builtin__.sage_prompt = sage_prompt
    569467
    570468
     469
     470#######################################
     471#
     472def load_a_file(argstr, globals):
     473    s = open(argstr).read()
     474    return preparse_file(s, globals=globals)
     475   
  • sage/misc/preparser.py

    diff --git a/sage/misc/preparser.py b/sage/misc/preparser.py
    a b def preparse(line, reset=True, do_time=F 
    921921## Apply the preparser to an entire file
    922922######################################################
    923923
    924 _attached={}
    925 def preparse_file(contents, attached=_attached, magic=True,
    926                   do_time=False, ignore_prompts=False,
    927                   numeric_literals=True, reload_attached=False):
     924attached = {}
     925def preparse_file(contents, globals=None, numeric_literals=True):
    928926    """
    929927    NOTE: Temporarily, if @parallel is in the input, then numeric_literals
    930     is always set to False.
     928    is always set to False.
     929
     930    INPUT:
     931
     932        - ``contents`` -- a string
     933        - ``globals`` -- dict or None (default: None); if given, then arguments to load/attach
     934          are evaluated in the namespace of this dict.   
     935        - ``numeric_literals`` -- bool (default: True), whether to factor out wrapping of
     936          integers and floats, so they don't get created repeatedly inside loops
    931937   
    932     TESTS:
     938    TESTS::
     939   
    933940        sage: from sage.misc.preparser import preparse_file
    934941        sage: lots_of_numbers = "[%s]" % ", ".join(str(i) for i in range(3000))
    935942        sage: _ = preparse_file(lots_of_numbers)
    936943        sage: preparse_file("type(100r), type(100)")
    937944        '_sage_const_100 = Integer(100)\ntype(100 ), type(_sage_const_100 )'
    938945    """
     946    if globals is None: globals = {}
     947   
    939948    if not isinstance(contents, str):
    940949        raise TypeError, "contents must be a string"
    941950
    942951    assert isinstance(contents, str)
    943952
    944     if reload_attached:
    945         # add lines to load each attached file that has changed
    946         for F, tm in attached.iteritems():
    947             if os.path.exists(F) and os.path.getmtime(F) > tm:
    948                 contents = 'load "%s"\n'%F + contents
     953    # Reload attached files that have changed
     954    for F, tm in attached.iteritems():
     955        new_tm = os.path.getmtime(F)
     956        if os.path.exists(F) and new_tm > tm:
     957            contents = 'load "%s"\n'%F + contents
     958        attached[F] = new_tm
    949959   
    950960    # We keep track of which files have been loaded so far
    951961    # in order to avoid a recursive load that would result
    def preparse_file(contents, attached=_at 
    975985            else:
    976986                contents = "\n".join(assignments) + "\n\n" + contents
    977987
     988    # The list F contains the preparsed lines so far.
    978989    F = []
     990    # A is the input, as a list of lines.
    979991    A = contents.splitlines()
     992    # We are currently parsing the i-th input line.
    980993    i = 0
    981994    while i < len(A):
    982         L = A[i].rstrip()
    983         if magic and L[:7] == "attach ":
    984             name = os.path.abspath(_strip_quotes(L[7:]))
    985             try:
    986                 if not attached.has_key(name):
    987                     t = os.path.getmtime(name)
    988                     attached[name] = t
    989             except IOError, OSError:
    990                 pass
    991             L = 'load ' + L[7:]
    992 
    993         if magic and L[:5] == "load ":
    994             try:
    995                 name_load = _strip_quotes(L[5:])
    996             except:
    997                 name_load = L[5:].strip()
    998             if name_load in loaded_files:
    999                 i += 1
    1000                 continue
    1001             loaded_files.append(name_load)
    1002             if name_load[-3:] == '.py':
    1003                 import IPython.ipapi
    1004                 # This is a dangerous hack, actually, since it means
    1005                 # that if the input file is:
    1006                 #     load a.sage
    1007                 #     load b.py
    1008                 # Then b.py will be loaded while the file is being *parsed*,
    1009                 # and *before* a.sage is loaded.  That would be very confusing.
    1010                 # This is now Trac ticket #4474.
    1011                 IPython.ipapi.get().magic('run -i "%s"'%name_load)
    1012                 L = ''
    1013             elif name_load[-5:] == '.sage':
    1014                 try:
    1015                     G = open(name_load)
    1016                 except IOError:
    1017                     print "File '%s' not found, so skipping load of %s"%(name_load, name_load)
    1018                     i += 1
     995        L = A[i]
     996        do_preparse = True
     997        for cmd in ['load', 'attach']:
     998            if L.lstrip().startswith(cmd+' '):
     999                j = L.find(cmd+' ')
     1000                s = L[j+len(cmd)+1:].strip()
     1001                if not s.startswith('('):
     1002                    F.append(' '*j + load_wrap(s, cmd=='attach'))
     1003                    do_preparse = False
    10191004                    continue
    1020                 else:
    1021                     A = A[:i] + G.readlines() + A[i+1:]
    1022                     continue
    1023             elif name_load[-5:] == '.spyx':
    1024                 import interpreter
    1025                 L = interpreter.load_cython(name_load)
    1026             else:
    1027                 #print "Loading of '%s' not implemented (load .py, .spyx, and .sage files)"%name_load
    1028                 L = 'load("%s")'%name_load
    1029 
    1030         M = preparse(L, reset=(i==0), do_time=do_time, ignore_prompts=ignore_prompts, numeric_literals=not numeric_literals)
    1031         F.append(M)
     1005        if do_preparse:
     1006            F.append(preparse(L, reset=(i==0), do_time=True, ignore_prompts=False,
     1007                              numeric_literals=not numeric_literals))
    10321008        i += 1
    1033     # end while
    10341009
    10351010    return '\n'.join(F)
    10361011
    class BackslashOperator: 
    11671142            (0.0, 0.5, 1.0, 1.5, 2.0)
    11681143        """
    11691144        return self.left._backslash_(right)
     1145
     1146
     1147def is_loadable_filename(filename):
     1148    """
     1149    Returns True if filename ends in one of the supported extensions for files that
     1150    can be loaded into Sage.
     1151
     1152    INPUT:
     1153
     1154        - ``filename`` -- a string
     1155
     1156    EXAMPLES::
     1157
     1158        sage: sage.misc.preparser.is_loadable_filename('foo.bar')
     1159        False
     1160        sage: sage.misc.preparser.is_loadable_filename('foo.c')
     1161        False
     1162        sage: sage.misc.preparser.is_loadable_filename('foo.sage')
     1163        True
     1164    """
     1165    # Loop over list of supported code file extensions for the load function.   
     1166    for ext in ['.py', '.pyx', '.sage', '.spyx']:
     1167        if filename.endswith(ext):
     1168            return True
     1169    return False
     1170
     1171def load(filename, globals, attach=False):
     1172    """
     1173    Execute the file with the given name in the scope specified by
     1174    globals (except in case of Cython, where the situation is more
     1175    complicated -- the module is compiled to a temporary module t and
     1176    ``from t import *`` is executed.).  If the filename starts with
     1177    http:// then it is treated as a URL and downloaded first.
     1178    Finally, the filename is first evaluated as an expression that
     1179    evaluates in the globals scope to a filename.
     1180
     1181    INPUT:
     1182
     1183        - ``filename`` -- a .py, .sage, .pyx, etc., filename or expression
     1184          that evaluates to one....
     1185        - ``globals`` -- a dictionary
     1186        - ``attach`` -- bool (default: False); if True, the filename
     1187          is added to the attached files list
     1188
     1189    EXAMPLES::
     1190
     1191    Note that .py files are not preparsed::
     1192   
     1193        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hi',2/3; z=-2/9")
     1194        sage: sage.misc.preparser.load(t,globals())
     1195        hi 0
     1196        sage: z
     1197        -1
     1198
     1199    Python files are preparsed::
     1200   
     1201        sage: t=tmp_filename()+'.sage'; open(t,'w').write("print 'hi',2/3; s=-2/7")
     1202        sage: sage.misc.preparser.load(t,globals())
     1203        hi 2/3
     1204        sage: s
     1205        -2/7
     1206
     1207    A Cython file::
     1208   
     1209        sage: t=tmp_filename()+'.pyx'; open(t,'w').write("print 'hi',2/3; z=-2/9")
     1210        sage: z=0; sage.misc.preparser.load(t,globals())
     1211        Compiling ....pyx...
     1212        hi 0
     1213        sage: z
     1214        -1
     1215
     1216    If the file isn't a Cython, Python, or a Sage file, a ValueError is raised::
     1217
     1218        sage: sage.misc.preparser.load('a.foo',globals())
     1219        Traceback (most recent call last):
     1220        ...
     1221        ValueError: argument (='a.foo') to load or attach must have extension py, sage, or pyx
     1222
     1223    A filename that is given as an expression (that gets evaluated).
     1224    This is important in making it so that ``load DATA+'foo.sage'``
     1225    works, say::
     1226
     1227        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1228        sage: sage.misc.preparser.load(t, globals())
     1229        hello world
     1230
     1231    We load a file given at a remote URL::
     1232
     1233        sage: sage.misc.preparser.load('http://wstein.org/loadtest.py', globals())  # optional - internet
     1234        hi from the net
     1235
     1236    We attach a file::
     1237
     1238        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1239        sage: sage.misc.preparser.load(t, globals(), attach=True)
     1240        hello world
     1241        sage: t in sage.misc.preparser.attached
     1242        True
     1243
     1244    You can't attach remote URL's (yet)::
     1245
     1246        sage: sage.misc.preparser.load('http://wstein.org/loadtest.py', globals(), attach=True)  # optional - internet
     1247        Traceback (most recent call last):
     1248        ...
     1249        NotImplementedError: you can't attach a URL
     1250    """
     1251    try:
     1252        filename = eval(filename, globals)
     1253    except Exception:
     1254        # handle multiple input files separated by spaces, which was
     1255        # maybe a bad idea, but which we have to handle for backwards
     1256        # compatibility.
     1257        v = filename.split()
     1258        if len(v) > 1:
     1259            for file in v:
     1260                load(file, globals, attach=attach)
     1261            return
     1262       
     1263    filename = filename.strip()
     1264   
     1265    if filename.lower().startswith('http://'):
     1266        if attach:
     1267            # But see http://en.wikipedia.org/wiki/HTTP_ETag for how we will do this.
     1268            # http://www.diveintopython.org/http_web_services/etags.html
     1269            raise NotImplementedError, "you can't attach a URL"
     1270        from remote_file import get_remote_file
     1271        filename = get_remote_file(filename, verbose=False)
     1272
     1273    if filename.endswith('.py'):
     1274        execfile(filename, globals)
     1275    elif filename.endswith('.sage'):
     1276        exec(preparse_file(open(filename).read()), globals)
     1277    elif filename.endswith('.spyx') or filename.endswith('.pyx'):
     1278        import interpreter
     1279        exec(interpreter.load_cython(filename), globals)
     1280    elif filename.endswith('.m'):
     1281        # Assume magma for now, though maybe .m is used by maple and
     1282        # mathematica too, and we should really analyze the file
     1283        # further.
     1284        s = globals['magma'].load(filename)
     1285        i = s.find('\n'); s = s[i+1:]
     1286        print s
     1287    else:
     1288        raise ValueError, "argument (='%s') to load or attach must have extension py, sage, or pyx"%filename
     1289
     1290    if attach and os.path.exists(filename):
     1291        attached[filename] = os.path.getmtime(filename)
     1292
     1293
     1294import base64   
     1295def load_wrap(filename, attach=False):
     1296    """
     1297    Internal function used to encode a load command as valid Python.
     1298    filename is the argument to the load or attach command, and attach
     1299    is True if the command is attach (rather than load).
     1300
     1301    INPUT:
     1302
     1303        - ``filename`` -- a string
     1304        - ``attach`` -- bool (default: False)
     1305
     1306    EXAMPLES::
     1307
     1308        sage: sage.misc.preparser.load_wrap('foo.py', True)
     1309        'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("Zm9vLnB5"),globals(),True)'
     1310        sage: sage.misc.preparser.load_wrap('foo.sage')
     1311        'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("Zm9vLnNhZ2U="),globals(),False)'
     1312        sage: sage.misc.preparser.base64.b64decode("Zm9vLnNhZ2U=")
     1313        'foo.sage'
     1314    """
     1315    return 'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("%s"),globals(),%s)'%(
     1316        base64.b64encode(filename), attach)
     1317
     1318
     1319def modified_attached_files():
     1320    """
     1321    Return iterator over the names of the attached files that have
     1322    changed since last time this function was called.
     1323
     1324    EXAMPLES::
     1325   
     1326        sage: sage.misc.reset.reset_attached()
     1327        sage: t=tmp_filename()+'.py';
     1328        sage: a = 0
     1329        sage: f = open(t,'w'); f.write("a = 5"); f.close()
     1330        sage: attach(t)
     1331        sage: a
     1332        5
     1333        sage: len(list(sage.misc.preparser.modified_attached_files()))
     1334        0
     1335        sage: import time; time.sleep(2)
     1336        sage: f = open(t,'w'); f.write("a = 10"); f.close()
     1337        sage: len(list(sage.misc.preparser.modified_attached_files()))
     1338        1
     1339        sage: len(list(sage.misc.preparser.modified_attached_files()))
     1340        0
     1341    """
     1342    # A generator is the perfect data structure here, since something
     1343    # could go wrong loading one file, and we end up only marking the
     1344    # ones that we returned as loaded.
     1345    for F in attached.keys():
     1346        tm = attached[F]
     1347        new_tm = os.path.getmtime(F)
     1348        if os.path.exists(F) and new_tm > tm:
     1349            # F is newer than last time we loaded it.
     1350            attached[F] = os.path.getmtime(F)
     1351            yield F
     1352   
     1353def attached_files():
     1354    """
     1355    Return a list of all files attached to the current session.
     1356
     1357    OUTPUT:
     1358
     1359        -- sorted list of filenames
     1360
     1361    EXAMPLES::
     1362
     1363        sage: sage.misc.reset.reset_attached()
     1364        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1365        sage: attach(t)
     1366        hello world
     1367        sage: attached_files() == [t]
     1368        True
     1369   
     1370    """
     1371    return list(sorted(attached.keys()))
     1372
     1373def detach(filename):
     1374    """
     1375    Detach that file with the given filename, if it was attached with
     1376    the attach command.
     1377
     1378    INPUT:
     1379
     1380       - ``filename`` -- string
     1381
     1382    EXAMPLES::
     1383
     1384
     1385        sage: sage.misc.reset.reset_attached()
     1386        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1387        sage: attach(t)
     1388        hello world
     1389        sage: attached_files() == [t]
     1390        True
     1391        sage: detach(t)
     1392        sage: attached_files()
     1393        []
     1394    """
     1395    if attached.has_key(filename):
     1396        del attached[filename]
     1397
  • sage/misc/reset.pyx

    diff --git a/sage/misc/reset.pyx b/sage/misc/reset.pyx
    a b def reset(vars=None): 
    3838                pass
    3939    restore()
    4040    reset_interfaces()
     41    reset_attached()
    4142
    4243def restore(vars=None):
    4344    """
    def reset_interfaces(): 
    118119    from sage.interfaces.quit import expect_quitall
    119120    expect_quitall()
    120121   
    121 ##     import sys
    122 ##     M = sys.modules
    123 ##     for k in M.keys():
    124 ##         if 'sage.interfaces' in k:
    125 ##             if not M[k] is None:
    126 ##                 reload(M[k])
     122def reset_attached():
     123    """
     124    Remove all the attached files from the list of attached files.
    127125
    128 ##     import sage.interfaces.all
    129 ##     G = globals()
    130 ##     for k, v in sage.interfaces.all.__dict__.iteritems():
    131 ##         G[k] = v
     126    EXAMPLES::
     127   
     128        sage: sage.misc.reset.reset_attached()
     129        sage: t = tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     130        sage: attach(t)
     131        hello world
     132        sage: attached_files() == [t]
     133        True
     134        sage: sage.misc.reset.reset_attached()
     135        sage: attached_files()
     136        []
     137    """
     138    import preparser
     139    preparser.attached = {}
    132140
    133 
  • sage/misc/sageinspect.py

    diff --git a/sage/misc/sageinspect.py b/sage/misc/sageinspect.py
    a b Cython classes:: 
    5353
    5454Python classes::
    5555
     56    sage: import sage.misc.attach
    5657    sage: sage_getfile(sage.misc.attach.Attach)
    5758    '.../attach.py'
    5859
    5960    sage: sage_getdoc(sage.misc.attach.Attach).lstrip()
    60     "Attach a file to a running instance of Sage..."
     61    'Attach a file to a running instance of Sage...'
    6162
    6263    sage: sage_getsource(sage.misc.attach.Attach)
    6364    'class Attach:...'
  • sage/misc/session.pyx

    diff --git a/sage/misc/session.pyx b/sage/misc/session.pyx
    a b def load_session(name='sage_session', ve 
    319319    for k, x in D.items():
    320320        state[k] = x
    321321
     322
     323def attach(*files):
     324    """
     325    Attach a file or files to a running instance of Sage and also load
     326    that file.
     327   
     328    .. note::
     329
     330       In addition to ``attach('foo.sage')``, from the sage prompt
     331       you can also just type ``attach "foo.sage"``.  However this
     332       second common usage is not part of the Python language.
     333   
     334    ``load`` is the same as attach, but doesn't automatically reload a
     335    file when it changes.
     336   
     337    You attach a file, e.g., ``foo.sage`` or ``foo.py`` or
     338    ``foo.pyx``, to a running Sage session by typing::
     339   
     340        sage: attach foo.sage   # or foo.py   or foo.pyx or even a URL to such a file (not tested)
     341
     342    or::
     343   
     344        sage: attach('foo.sage')  # not tested
     345
     346    Here we test attaching multiple files at once::
     347   
     348        sage: sage.misc.reset.reset_attached()
     349        sage: t1=tmp_filename()+'.py'; open(t1,'w').write("print 'hello world'")
     350        sage: t2=tmp_filename()+'.py'; open(t2,'w').write("print 'hi there xxx'")
     351        sage: attach(t1, t2)
     352        hello world
     353        hi there xxx
     354        sage: attached_files() == [t1,t2]
     355        True
     356       
     357   
     358    The contents of the file are then loaded, which means they are
     359    read into the running Sage session. For example, if ``foo.sage``
     360    contains ``x=5``, after attaching ``foo.sage`` the variable ``x``
     361    will be set to 5. Moreover, any time you change ``foo.sage``,
     362    before you execute a command, the attached file will be re-read
     363    automatically (with no intervention on your part).
     364   
     365    USAGE: ``attach file1 ...`` - space-separated list of .py, .pyx,
     366    and .sage files.
     367   
     368    EFFECT: Each file is read in and added to an internal list of
     369    watched files. The meaning of reading a file in depends on the file
     370    type:
     371   
     372   
     373    -  read in with no preparsing (so, e.g., ``23`` is 2
     374       bit-xor 3),
     375   
     376    -  preparsed then the result is read in
     377   
     378    -  *not* preparsed. Compiled to a module ``m`` then
     379       ``from m import *`` is executed.
     380   
     381   
     382    Type ``attached_files()`` for a list of all currently
     383    attached files. You can remove files from this list to stop them
     384    from being watched.
     385   
     386    .. note::
     387
     388        ``attach`` is exactly the same as load, except it keeps track of
     389        the loaded file and automatically reloads it when it changes.
     390    """
     391    import preparser
     392    for filename in files:
     393        preparser.load(filename, globals(), attach=True)
  • sage/structure/sage_object.pyx

    diff --git a/sage/structure/sage_object.pyx b/sage/structure/sage_object.pyx
    a b def have_same_parent(self, other): 
    615615
    616616##################################################################
    617617
    618 def load(filename, compress=True, verbose=True):
     618def load(*filename, compress=True, verbose=True):
    619619    """   
    620     Load Sage object from the file with name filename, which will
    621     have an .sobj extension added if it doesn't have one.
     620    Load Sage object from the file with name filename, which will have
     621    an .sobj extension added if it doesn't have one.  Or, if the input
     622    is a filename ending in .py, .pyx, or .sage, load that file into
     623    the current running session.  Loaded files are not loaded into
     624    their own namespace, i.e., this is much more like Python's
     625    ``execfile`` than Python's ``import``.
    622626
    623627    .. note::
    624628
    def load(filename, compress=True, verbos 
    633637       documentation for load is almost identical to that for attach.
    634638       Type attach? for help on attach.
    635639
    636     This also loads a ".sobj" file over a network by specifying the full URL.
    637     (Setting "verbose = False" suppresses the loading progress indicator.)   
     640    This function also loads a ``.sobj`` file over a network by
     641    specifying the full URL.  (Setting ``verbose = False`` suppresses
     642    the loading progress indicator.)
     643
     644    Finally, if you give multiple positional input arguments, then all
     645    of those files are loaded, or all of the objects are loaded and a
     646    list of the corresponding loaded objects is returned.
    638647
    639648    EXAMPLE::
    640649   
    def load(filename, compress=True, verbos 
    644653        Loading: [.]       
    645654        sage: s                                                            # optional - internet
    646655        'hello SAGE'
     656
     657    We test loading a file or multiple files or even mixing loading files and objects::
     658   
     659        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     660        sage: load(t)
     661        hello world
     662        sage: load(t,t)
     663        hello world
     664        hello world
     665        sage: t2=tmp_filename(); save(2/3,t2)
     666        sage: load(t,t,t2)
     667        hello world
     668        hello world
     669        [None, None, 2/3]
    647670    """
    648 
     671    import sage.misc.preparser
     672    if len(filename) != 1:
     673        v = [load(file, compress=True, verbose=True) for file in filename]
     674        ret = False
     675        for file in filename:
     676            if not sage.misc.preparser.is_loadable_filename(file):
     677                ret = True
     678        return v if ret else None
     679    else:
     680        filename = filename[0]
     681   
     682    if filename.endswith('.sage') or filename.endswith('.py') or filename.endswith('.pyx'):
     683        sage.misc.preparser.load(filename, globals())
     684        return
     685   
    649686    ## Check if filename starts with "http://" or "https://"
    650687    lower = filename.lower()
    651688    if lower.startswith("http://") or lower.startswith("https://"):