Ticket #7514: sagelib-7514.patch

File sagelib-7514.patch, 31.8 KB (added by was, 11 years ago)

apply to the core sage library

  • sage/misc/all.py

    # HG changeset patch
    # User William Stein <wstein@gmail.com>
    # Date 1258877567 28800
    # Node ID ae015a42d3253b33bd83050a40c913bc6d2b3df4
    # Parent  2f804557165ea874d9b1034627f81a1d022776a7
    trac 7514 -- rewrite load and attach
    
    diff -r 2f804557165e -r ae015a42d325 sage/misc/all.py
    a b  
    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
     
    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
  • deleted file sage/misc/attach.py

    diff -r 2f804557165e -r ae015a42d325 sage/misc/attach.py
    + -  
    1 """
    2 Attach a file to a running instance of Sage.
    3 """
    4 
    5 class Attach:
    6     r"""
    7     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.
    57     """
    58     def __repr__(self):
    59         return self.__doc__
    60 
    61     def __call__(self, *args, **kwds):
    62         raise RuntimeError, "Use 'attach filename' instead, where filename is a .py, .sage, or .spyx file."
    63 
    64 attach = Attach()
  • sage/misc/interpreter.py

    diff -r 2f804557165e -r ae015a42d325 sage/misc/interpreter.py
    a b  
    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
     
    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)
     
    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.
     
    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:
     
    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:]))
     
    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):
     
    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')
     
    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 -r 2f804557165e -r ae015a42d325 sage/misc/preparser.py
    a b  
    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
     
    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
     
    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 /Users/wstein/.sage//temp/flat.local/8881//tmp_4.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: D = 'a.'
     1228        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1229        sage: sage.misc.preparser.load("D + 'py'",globals())
     1230        hi 0
     1231
     1232    We load a file given at a remote URL::
     1233
     1234        sage: sage.misc.preparser.load('http://wstein.org/loadtest.py', globals())  # optional - internet
     1235        hi from the net
     1236
     1237    We attach a file::
     1238
     1239        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1240        sage: sage.misc.preparser.load(t, globals(), attach=True)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 attach and os.path.exists(filename):
     1274        attached[filename] = os.path.getmtime(filename)
     1275       
     1276    if filename.endswith('.py'):
     1277        execfile(filename, globals)
     1278        return
     1279    if filename.endswith('.sage'):
     1280        exec(preparse_file(open(filename).read()), globals)
     1281        return
     1282    if filename.endswith('.spyx') or filename.endswith('.pyx'):
     1283        import interpreter
     1284        exec(interpreter.load_cython(filename), globals)
     1285        return
     1286    if filename.endswith('.m'):
     1287        # Assume magma for now, though maybe .m is used by maple and
     1288        # mathematica too, and we should really analyze the file
     1289        # further.
     1290        s = globals['magma'].load(filename)
     1291        i = s.find('\n'); s = s[i+1:]
     1292        print s
     1293        return
     1294    raise ValueError, "argument (='%s') to load or attach must have extension py, sage, or pyx"%filename
     1295
     1296import base64   
     1297def load_wrap(filename, attach=False):
     1298    """
     1299    Internal function used to encode a load command as valid Python.
     1300    filename is the argument to the load or attach command, and attach
     1301    is True if the command is attach (rather than load).
     1302
     1303    INPUT:
     1304
     1305        - ``filename`` -- a string
     1306        - ``attach`` -- bool (default: False)
     1307
     1308    EXAMPLES::
     1309
     1310        sage: sage.misc.preparser.load_wrap('foo.py', True)
     1311        'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("Zm9vLnB5"),globals(),True)'
     1312        sage: sage.misc.preparser.load_wrap('foo.sage')
     1313        'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("Zm9vLnNhZ2U="),globals(),False)'
     1314        sage: sage.misc.preparser.base64.b64decode("Zm9vLnNhZ2U=")
     1315        'foo.sage'
     1316    """
     1317    return 'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("%s"),globals(),%s)'%(
     1318        base64.b64encode(filename), attach)
     1319
     1320
     1321def modified_attached_files():
     1322    """
     1323    Return iterator over the names of the attached files that have
     1324    changed since last time this function was called.
     1325
     1326    EXAMPLES::
     1327   
     1328        sage: sage.misc.reset.reset_attached()
     1329        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1330        sage: attach t
     1331        hello world
     1332        sage: open(t,'w').write("print 'hi'")
     1333        sage: len(list(sage.misc.preparser.modified_attached_files()))
     1334        1
     1335        sage: len(list(sage.misc.preparser.modified_attached_files()))
     1336        0
     1337    """
     1338    # A generator is the perfect data structure here, since something
     1339    # could go wrong loading one file, and we end up only marking the
     1340    # ones that we returned as loaded.
     1341    for F in attached.keys():
     1342        tm = attached[F]
     1343        new_tm = os.path.getmtime(F)
     1344        if os.path.exists(F) and new_tm > tm:
     1345            # F is newer than last time we loaded it.
     1346            attached[F] = os.path.getmtime(F)
     1347            yield F
     1348   
     1349def attached_files():
     1350    """
     1351    Return a list of all files attached to the current session.
     1352
     1353    OUTPUT:
     1354
     1355        -- sorted list of filenames
     1356
     1357    EXAMPLES::
     1358
     1359        sage: sage.misc.reset.reset_attached()
     1360        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1361        sage: attach t
     1362        hello world
     1363        sage: attached_files() == [t]
     1364        True
     1365   
     1366    """
     1367    return list(sorted(attached.keys()))
     1368
     1369def detach(filename):
     1370    """
     1371    Detach that file with the given filename, if it was attached with
     1372    the attach command.
     1373
     1374    INPUT:
     1375
     1376       - ``filename`` -- string
     1377
     1378    EXAMPLES::
     1379
     1380
     1381        sage: sage.misc.reset.reset_attached()
     1382        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1383        sage: attach t
     1384        hello world
     1385        sage: attached_files() = [t]
     1386        True
     1387        sage: detach(t)
     1388        sage: attached_files()
     1389        []
     1390    """
     1391    if attached.has_key(filename):
     1392        del attached[filename]
     1393
  • sage/misc/reset.pyx

    diff -r 2f804557165e -r ae015a42d325 sage/misc/reset.pyx
    a b  
    3838                pass
    3939    restore()
    4040    reset_interfaces()
     41    reset_attached()
    4142
    4243def restore(vars=None):
    4344    """
     
    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()
     133        ['/Users/wstein/.sage//temp/flat.local/12411//tmp_0.py']
     134        sage: sage.misc.reset.reset_attached()
     135        sage: attached_files()
     136        []
     137    """
     138    import preparser
     139    preparser.attached = {}
    132140
    133 
  • sage/misc/session.pyx

    diff -r 2f804557165e -r ae015a42d325 sage/misc/session.pyx
    a b  
    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') 
     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 -r 2f804557165e -r ae015a42d325 sage/structure/sage_object.pyx
    a b  
    595595##################################################################
    596596   
    597597
    598 
    599 
    600 def load(filename, compress=True, verbose=True):
     598def load(*filename, compress=True, verbose=True):
    601599    """   
    602     Load Sage object from the file with name filename, which will
    603     have an .sobj extension added if it doesn't have one.
     600    Load Sage object from the file with name filename, which will have
     601    an .sobj extension added if it doesn't have one.  Or, if the input
     602    is a filename ending in .py, .pyx, or .sage, load that file into
     603    the current running session.  Loaded files are not loaded into
     604    their own namespace, i.e., this is much more like Python's
     605    ``execfile`` than Python's ``import``.
    604606
    605607    .. note::
    606608
     
    615617       documentation for load is almost identical to that for attach.
    616618       Type attach? for help on attach.
    617619
    618     This also loads a ".sobj" file over a network by specifying the full URL.
    619     (Setting "verbose = False" suppresses the loading progress indicator.)   
     620    This function also loads a ``.sobj`` file over a network by
     621    specifying the full URL.  (Setting ``verbose = False`` suppresses
     622    the loading progress indicator.)
     623
     624    Finally, if you give multiple positional input arguments, then all
     625    of those files are loaded, or all of the objects are loaded and a
     626    list of the corresponding loaded objects is returned.
    620627
    621628    EXAMPLE::
    622629   
     
    626633        Loading: [.]       
    627634        sage: s                                                            # optional - internet
    628635        'hello SAGE'
     636
     637    We test loading a file or multiple files or even mixing loading files and objects::
     638   
     639        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     640        sage: load(t)
     641        hello world
     642        sage: load(t,t)
     643        hello world
     644        hello world
     645        sage: t2=tmp_filename(); save(2/3,t2)
     646        sage: load(t,t,t2)
     647        hello world
     648        hello world
     649        [None, None, 2/3]
    629650    """
    630 
     651    import sage.misc.preparser
     652    if len(filename) != 1:
     653        v = [load(file, compress=True, verbose=True) for file in filename]
     654        ret = False
     655        for file in filename:
     656            if not sage.misc.preparser.is_loadable_filename(file):
     657                ret = True
     658        return v if ret else None
     659    else:
     660        filename = filename[0]
     661   
     662    if filename.endswith('.sage') or filename.endswith('.py') or filename.endswith('.pyx'):
     663        sage.misc.preparser.load(filename, globals())
     664        return
     665   
    631666    ## Check if filename starts with "http://" or "https://"
    632667    lower = filename.lower()
    633668    if lower.startswith("http://") or lower.startswith("https://"):