Ticket #14523: trac_14523_improve_attach.patch

File trac_14523_improve_attach.patch, 48.4 KB (added by Volker Braun, 9 years ago)

Updated patch

  • sage/misc/all.py

    # HG changeset patch
    # User Volker Braun <vbraun.name@gmail.com>
    # Date 1370725396 -3600
    # Node ID e71df55570510400a82eddfc2bc72d1a1e9526d4
    # Parent  727bdaee5dded6ee340bb9712b3feef3d7f2f044
    Improve attach()
    
    diff --git a/sage/misc/all.py b/sage/misc/all.py
    a b  
     1from lazy_attribute import lazy_attribute, lazy_class_attribute
     2from lazy_import import lazy_import
     3
    14from misc import (alarm, ellipsis_range, ellipsis_iter, srange, xsrange, sxrange, getitem,
    25                  cputime, verbose, set_verbose, set_verbose_files,
    36                  get_verbose_files, unset_verbose_files, get_verbose,
     
    2831
    2932from map_threaded import map_threaded
    3033
    31 from session import load_session, save_session, show_identifiers, attach
     34from session import load_session, save_session, show_identifiers
    3235
    3336from remote_file import get_remote_file
    3437
     
    6568
    6669from defaults import set_default_variable_name
    6770
    68 from preparser import preparse, implicit_multiplication, BackslashOperator, attached_files, detach, load_attach_path, reset_load_attach_path, load_attach_mode
     71from preparser import preparse, implicit_multiplication, BackslashOperator
     72
     73lazy_import('sage.misc.attached_files', [
     74        'attach', 'detach', 'attached_files', 'load_attach_path',
     75        'reset_load_attach_path', 'load_attach_mode'])
    6976
    7077from interpreter import preparser
    7178
     
    157164
    158165from cachefunc import CachedFunction, cached_function, cached_method, cached_in_parent_method, disk_cached_function
    159166
    160 from lazy_attribute import lazy_attribute, lazy_class_attribute
    161 
    162 
    163 from lazy_import import lazy_import
    164 
    165167from abstract_method import abstract_method
    166168
    167169from randstate import seed, set_random_seed, initial_seed, current_randstate
  • deleted file sage/misc/attach.py

    diff --git a/sage/misc/attach.py b/sage/misc/attach.py
    deleted file mode 100644
    + -  
    1 """
    2 Attach a file to a running instance of Sage.
    3 """
    4 
    5 class Attach:
    6     """
    7     Attach a file to a running instance of Sage.
    8     """
    9     def __repr__(self):
    10         return self.__doc__
    11 
    12     def __call__(self, *args, **kwds):
    13         raise RuntimeError, "Use 'attach filename' instead, where filename is a .py, .sage, or .spyx file."
    14 
    15 attach = Attach()
  • new file sage/misc/attached_files.py

    diff --git a/sage/misc/attached_files.py b/sage/misc/attached_files.py
    new file mode 100644
    - +  
     1"""
     2Keep track of attached files
     3
     4TESTS::
     5
     6    sage: attach('http://wstein.org/loadtest.py')
     7    Traceback (most recent call last):
     8    ...
     9    NotImplementedError: you can't attach a URL
     10
     11Check that no file clutter is produced::
     12
     13    sage: dir = tmp_dir()
     14    sage: src = os.path.join(dir, 'foobar.sage')
     15    sage: with open(src, 'w') as f:
     16    ....:     f.write('print "<output from attached file>"\n')
     17    sage: attach(src)
     18    <output from attached file>
     19    sage: os.listdir(dir)
     20    ['foobar.sage']
     21    sage: detach(src)
     22   
     23In debug mode backtraces contain code snippets. We need to manually
     24print the traceback because the python doctest module has special
     25support for exceptions and does not match them
     26character-by-character::
     27
     28    sage: import traceback
     29    sage: with open(src, 'w') as f:
     30    ....:     f.write('# first line\n')
     31    ....:     f.write('# second line\n')
     32    ....:     f.write('raise ValueError("third")   # this should appear in the source snippet\n')
     33    ....:     f.write('# fourth line\n')
     34
     35    sage: load_attach_mode(attach_debug=False)
     36    sage: try:
     37    ....:     attach(src)
     38    ....: except StandardError:
     39    ....:     traceback.print_exc()
     40    Traceback (most recent call last):
     41    ...
     42        exec preparse_file(open(fpath).read()) + "\n" in globals
     43      File "<string>", line 3, in <module>
     44    ValueError: third
     45    sage: detach(src)
     46
     47    sage: load_attach_mode(attach_debug=True)
     48    sage: try:
     49    ....:     attach(src)
     50    ....: except StandardError:
     51    ....:     traceback.print_exc()
     52    Traceback (most recent call last):
     53    ...
     54        execfile(preparse_file_named(fpath), globals)
     55      File ".../foobar.sage....py", line ..., in <module>
     56        raise ValueError("third")   # this should appear in the source snippet
     57    ValueError: third
     58    sage: detach(src)
     59"""
     60
     61###########################################################################
     62#       Copyright (C) 2013 Volker Braun <vbraun.name@gmail.com>
     63#
     64#  Distributed under the terms of the GNU General Public License (GPL)
     65#                  http://www.gnu.org/licenses/
     66###########################################################################
     67
     68import os
     69import time
     70from sage.misc.preparser import load, load_wrap
     71import sage.env
     72
     73# The attached files as a dict of {filename:mtime}
     74attached = {}
     75
     76
     77load_debug_mode = False
     78attach_debug_mode = True
     79
     80def load_attach_mode(load_debug=None, attach_debug=None):
     81    """
     82    Get or modify the current debug mode for the behavior of
     83    :func:`load` and :func:`attach` on ``.sage`` files.
     84
     85    In debug mode, loaded or attached ``.sage`` files are preparsed
     86    through a file to make their tracebacks more informative. If not
     87    in debug mode, then ``.sage`` files are preparsed in memory only
     88    for performance.
     89
     90    At startup, debug mode is ``True`` for attaching and ``False``
     91    for loading.
     92
     93    .. NOTE::
     94
     95        This function should really be deprecated and code executed
     96        from memory should raise proper tracebacks.
     97
     98    INPUT:
     99
     100    - ``load_debug`` -- boolean or ``None`` (default); if not
     101      ``None``, then set a new value for the debug mode for loading
     102      files.
     103
     104    - ``attach_debug`` -- boolean or ``None`` (default); same as
     105      ``load_debug``, but for attaching files.
     106
     107    OUTPUT:
     108
     109    If all input values are ``None``, returns a tuple giving the
     110    current modes for loading and attaching.
     111
     112    EXAMPLES::
     113
     114        sage: load_attach_mode()
     115        (False, True)
     116        sage: load_attach_mode(attach_debug=False)
     117        sage: load_attach_mode()
     118        (False, False)
     119        sage: load_attach_mode(load_debug=True)
     120        sage: load_attach_mode()
     121        (True, False)
     122        sage: load_attach_mode(load_debug=False, attach_debug=True)
     123    """
     124    global load_debug_mode, attach_debug_mode
     125    if load_debug is None and attach_debug is None:
     126        return (load_debug_mode, attach_debug_mode)
     127    if not load_debug is None:
     128        load_debug_mode = load_debug
     129    if not attach_debug is None:
     130        attach_debug_mode = attach_debug
     131
     132
     133search_paths = []
     134
     135def load_attach_path(path=None, replace=False):
     136    """
     137    Get or modify the current search path for :func:`load` and
     138    :func:`attach`.
     139
     140    INPUT:
     141
     142    - ``path`` -- string or list of strings (default: ``None``);
     143      path(s) to append to or replace the current path.
     144
     145    - ``replace`` -- boolean (default: ``False``); if ``path`` is not
     146      ``None``, whether to *replace* the search path instead of
     147      *appending* to it.
     148
     149    OUTPUT:
     150
     151    ``None`` or a *reference* to the current search paths.
     152
     153    EXAMPLES:
     154
     155    First, we extend the example given in :func:`load`'s docstring::
     156
     157        sage: sage.misc.attached_files.reset(); reset_load_attach_path()
     158        sage: load_attach_path()
     159        ['.']
     160        sage: t_dir = tmp_dir()
     161        sage: fullpath = os.path.join(t_dir, 'test.py')
     162        sage: open(fullpath, 'w').write("print 37 * 3")
     163        sage: attach('test.py')
     164        Traceback (most recent call last):
     165        ...
     166        IOError: did not find file 'test.py' in load / attach search path
     167        sage: load_attach_path(t_dir)
     168        sage: attach('test.py')
     169        111
     170        sage: attached_files() == [fullpath]
     171        True
     172        sage: sage.misc.attached_files.reset(); reset_load_attach_path()
     173        sage: load_attach_path() == ['.']
     174        True
     175        sage: load('test.py')
     176        Traceback (most recent call last):
     177        ...
     178        IOError: did not find file 'test.py' in load / attach search path
     179
     180    The function returns a reference to the path list::
     181
     182        sage: reset_load_attach_path(); load_attach_path()
     183        ['.']
     184        sage: load_attach_path('/path/to/my/sage/scripts'); load_attach_path()
     185        ['.', '/path/to/my/sage/scripts']
     186        sage: load_attach_path(['good', 'bad', 'ugly'], replace=True)
     187        sage: load_attach_path()
     188        ['good', 'bad', 'ugly']
     189        sage: p = load_attach_path(); p.pop()
     190        'ugly'
     191        sage: p[0] = 'weird'; load_attach_path()
     192        ['weird', 'bad']
     193        sage: reset_load_attach_path(); load_attach_path()
     194        ['.']
     195    """
     196    global search_paths
     197    if path is None:
     198        return search_paths
     199    else:
     200        if isinstance(path, basestring):
     201            path = [path]
     202        if replace:
     203            search_paths = path
     204        else:
     205            for p in path:
     206                if not p:
     207                    continue
     208                if p not in search_paths:
     209                    search_paths.append(p)
     210
     211
     212def reset_load_attach_path():
     213    """
     214    Resets the current search path for :func:`load` and
     215    :func:`attach`.
     216
     217    The default path is ``'.'`` plus any paths specified in the
     218    environment variable ``SAGE_LOAD_ATTACH_PATH``.
     219
     220    EXAMPLES::
     221
     222        sage: load_attach_path()
     223        ['.']
     224        sage: t_dir = tmp_dir()
     225        sage: load_attach_path(t_dir)
     226        sage: t_dir in load_attach_path()
     227        True
     228        sage: reset_load_attach_path(); load_attach_path()
     229        ['.']
     230
     231    At startup, Sage adds colon-separated paths in the environment
     232    variable ``SAGE_LOAD_ATTACH_PATH``::
     233
     234        sage: reset_load_attach_path(); load_attach_path()
     235        ['.']
     236        sage: os.environ['SAGE_LOAD_ATTACH_PATH'] = '/veni/vidi:vici:'
     237        sage: reload(sage.misc.attached_files)    # Simulate startup
     238        <module 'sage.misc.attached_files' from '...'>
     239        sage: load_attach_path()
     240        ['.', '/veni/vidi', 'vici']
     241        sage: del os.environ['SAGE_LOAD_ATTACH_PATH']
     242        sage: reload(sage.misc.preparser)    # Simulate startup
     243        <module 'sage.misc.preparser' from '...'>
     244        sage: reset_load_attach_path(); load_attach_path()
     245        ['.']
     246    """
     247    global search_paths
     248    search_paths = ['.']
     249    for path in os.environ.get('SAGE_LOAD_ATTACH_PATH', '').split(':'):
     250        load_attach_path(path=path)
     251
     252# Set up the initial search path for loading and attaching files.  A
     253# user can modify the path with the function load_attach_path.
     254reset_load_attach_path()
     255
     256
     257def attach(*files):
     258    """
     259    Attach a file or files to a running instance of Sage and also load
     260    that file.
     261
     262    USAGE:
     263
     264    ``attach file1 ...`` - space-separated list of ``.py``, ``.pyx``,
     265    and ``.sage`` files, or ``attach('file1', 'file2')`` - filenames as
     266    strings, given as arguments to :func:`attach`.
     267
     268    :meth:`~sage.misc.preparser.load` is the same as :func:`attach`, but
     269    doesn't automatically reload a file when it changes.
     270
     271    .. NOTE::
     272
     273       On the Sage prompt you can also just type ``attach "foo.sage"``
     274       as a short-hand for ``attach('foo.sage')``. However this
     275       alternate form is not part of the Python language and does not
     276       work in Python scripts.
     277
     278    EFFECT:
     279
     280    Each file is read in and added to an internal list of watched files.
     281    The meaning of reading in a file depends on the file type:
     282
     283    -  ``.py`` files are read in with no preparsing (so, e.g., ``2^3`` is 2
     284       bit-xor 3);
     285
     286    -  ``.sage`` files are preparsed, then the result is read in;
     287
     288    - ``.pyx`` files are *not* preparsed, but rather are compiled to a
     289       module ``m`` and then ``from m import *`` is executed.
     290
     291    The contents of the file are then loaded, which means they are read
     292    into the running Sage session. For example, if ``foo.sage`` contains
     293    ``x=5``, after attaching ``foo.sage`` the variable ``x`` will be set
     294    to 5. Moreover, any time you change ``foo.sage``, before you execute
     295    a command, the attached file will be re-read automatically (with no
     296    intervention on your part).
     297
     298    EXAMPLES:
     299
     300    You attach a file, e.g., ``foo.sage`` or ``foo.py`` or
     301    ``foo.pyx``, to a running Sage session by typing::
     302
     303        sage: attach foo.sage   # or foo.py or foo.pyx or even a URL to such a file (not tested)
     304
     305    or::
     306
     307        sage: attach('foo.sage')  # not tested
     308
     309    Here we test attaching multiple files at once::
     310
     311        sage: sage.misc.attached_files.reset()
     312        sage: t1 = tmp_filename(ext='.py')
     313        sage: open(t1,'w').write("print 'hello world'")
     314        sage: t2 = tmp_filename(ext='.py')
     315        sage: open(t2,'w').write("print 'hi there xxx'")
     316        sage: attach(t1, t2)
     317        hello world
     318        hi there xxx
     319        sage: set(attached_files()) == set([t1,t2])
     320        True
     321
     322    .. SEEALSO::
     323
     324        - :meth:`attached_files` returns a list of
     325          all currently attached files.
     326
     327        - :meth:`detach` instructs Sage to remove a
     328          file from the internal list of watched files.
     329
     330        - :meth:`load_attach_path` allows you to
     331          get or modify the current search path for loading and attaching
     332          files.
     333    """
     334    try:
     335        ipy = get_ipython()
     336    except NameError:
     337        ipy = None
     338    global attached
     339    for filename in files:
     340        if ipy:
     341            code = load_wrap(filename, attach=True)
     342            ipy.run_cell(code)
     343        else:
     344            load(filename, globals(), attach=True)
     345
     346
     347def add_attached_file(filename):
     348    """
     349    Add to the list of attached files
     350   
     351    This is a callback to be used from
     352    :func:`~sage.misc.preparse.load` after evaluating the attached
     353    file the first time.
     354
     355    INPUT:
     356
     357    - ``filename`` -- string, the fully qualified file name.
     358
     359    EXAMPLES::
     360   
     361        sage: import sage.misc.attached_files as af
     362        sage: af.reset()
     363        sage: t = tmp_filename(ext='.py')
     364        sage: af.add_attached_file(t)
     365        sage: af.attached_files()
     366        ['/.../tmp_....py']
     367        sage: af.detach(t)
     368        sage: af.attached_files()
     369        []
     370    """
     371    fpath = os.path.abspath(filename)
     372    attached[fpath] = os.path.getmtime(fpath)
     373
     374
     375def attached_files():
     376    """
     377    Returns a list of all files attached to the current session with
     378    :meth:`attach`.
     379
     380    OUTPUT:
     381
     382    The filenames in a sorted list of strings.
     383
     384    EXAMPLES::
     385
     386        sage: sage.misc.attached_files.reset()
     387        sage: t = tmp_filename(ext='.py')
     388        sage: open(t,'w').write("print 'hello world'")
     389        sage: attach(t)
     390        hello world
     391        sage: attached_files()
     392        ['/....py']
     393        sage: attached_files() == [t]
     394        True
     395    """
     396    global attached
     397    return list(sorted(attached.keys()))
     398
     399
     400def detach(filename):
     401    """
     402    Detach a file.
     403
     404    This is the counterpart to :meth:`attach`.
     405
     406    INPUT:
     407
     408    - ``filename`` -- a string, or a list of strings, or a tuple of strings.
     409
     410    EXAMPLES::
     411
     412        sage: sage.misc.attached_files.reset()
     413        sage: t = tmp_filename(ext='.py')
     414        sage: open(t,'w').write("print 'hello world'")
     415        sage: attach(t)
     416        hello world
     417        sage: attached_files() == [t]
     418        True
     419        sage: detach(t)
     420        sage: attached_files()
     421        []
     422   
     423        sage: sage.misc.attached_files.reset(); reset_load_attach_path()
     424        sage: load_attach_path()
     425        ['.']
     426        sage: t_dir = tmp_dir()
     427        sage: fullpath = os.path.join(t_dir, 'test.py')
     428        sage: open(fullpath, 'w').write("print 37 * 3")
     429        sage: load_attach_path(t_dir)
     430        sage: attach('test.py')
     431        111
     432        sage: attached_files() == [os.path.normpath(fullpath)]
     433        True
     434        sage: detach('test.py')
     435        sage: attached_files()
     436        []
     437        sage: attach('test.py')
     438        111
     439        sage: fullpath = os.path.join(t_dir, 'test2.py')
     440        sage: open(fullpath, 'w').write("print 3")
     441        sage: attach('test2.py')
     442        3
     443        sage: detach(attached_files())
     444        sage: attached_files()
     445        []
     446
     447    TESTS::
     448
     449        sage: detach('/dev/null/foobar.sage')
     450        Traceback (most recent call last):
     451        ...
     452        ValueError: file '/dev/null/foobar.sage' is not attached, see attached_files()
     453    """
     454    if isinstance(filename, basestring):
     455        filelist = [filename]
     456    else:
     457        filelist = [str(x) for x in filename]
     458
     459    global attached
     460    for filename in filelist:
     461        fpath = os.path.expanduser(filename)
     462        if not os.path.isabs(fpath):
     463            for path in load_attach_path():
     464                epath = os.path.expanduser(path)
     465                fpath = os.path.join(epath, filename)
     466                fpath = os.path.abspath(fpath)
     467                if fpath in attached:
     468                    break
     469        if fpath in attached:
     470            attached.pop(fpath)
     471        else:
     472            raise ValueError("file '{0}' is not attached, see attached_files()".format(filename))
     473   
     474def reset():
     475    """
     476    Remove all the attached files from the list of attached files.
     477
     478    EXAMPLES::
     479   
     480        sage: sage.misc.attached_files.reset()
     481        sage: t = tmp_filename(ext='.py')
     482        sage: open(t,'w').write("print 'hello world'")
     483        sage: attach(t)
     484        hello world
     485        sage: attached_files() == [t]
     486        True
     487        sage: sage.misc.attached_files.reset()
     488        sage: attached_files()
     489        []
     490    """
     491    global attached
     492    attached = {}
     493
     494
     495def modified_file_iterator():
     496    """
     497    Iterate over the changed files
     498
     499    As a side effect the stored time stamps are updated with the
     500    actual time stamps. So if you iterate over the attached files in
     501    order to reload them and you hit an error then the subsequent
     502    files are not marked as read.
     503
     504    Files that are in the process of being saved are excluded.
     505
     506    EXAMPLES::
     507
     508        sage: sage.misc.attached_files.reset()
     509        sage: t = tmp_filename(ext='.py')
     510        sage: attach(t)
     511        sage: from sage.misc.attached_files import modified_file_iterator
     512        sage: list(modified_file_iterator())
     513        []
     514        sage: sleep(1)   # filesystem mtime granularity
     515        sage: open(t, 'w').write('1')
     516        sage: list(modified_file_iterator())
     517        [('/.../tmp_....py', time.struct_time(...))]
     518    """
     519    global attached
     520    modified = dict()
     521    for filename in attached.keys():
     522        old_tm = attached[filename]
     523        if not os.path.exists(filename):
     524            print '### detaching file {0} because it does not exist (deleted?) ###'.format(filename)
     525            detach(filename)
     526            continue
     527        new_tm = os.path.getmtime(filename)
     528        if new_tm > old_tm:
     529            modified[filename] = new_tm
     530
     531    if not modified:
     532        return
     533    time.sleep(0.1)  # sleep 100ms to give the editor time to finish saving
     534       
     535    for filename in modified.keys():
     536        old_tm = modified[filename]
     537        new_tm = os.path.getmtime(filename)
     538        if new_tm == old_tm:   
     539            # file was modified but did not change in the last 100ms
     540            attached[filename] = new_tm
     541            yield filename, time.gmtime(new_tm)
     542
     543
     544def reload_attached_files_if_modified():
     545    """
     546    Reload attached files that have been modified
     547   
     548    This is the internal implementation of the attach mechanism.
     549
     550    EXAMPLES::
     551   
     552        sage: sage.misc.attached_files.reset()
     553        sage: from sage.misc.interpreter import get_test_shell
     554        sage: shell = get_test_shell()
     555        sage: tmp = tmp_filename(ext='.py')
     556        sage: open(tmp, 'w').write('a = 2\n')
     557        sage: shell.run_cell('attach({0})'.format(repr(tmp)))
     558        sage: shell.run_cell('a')
     559        2
     560        sage: sleep(1)   # filesystem mtime granularity
     561        sage: open(tmp, 'w').write('a = 3\n')
     562
     563    Note that the doctests are never really at the command prompt
     564    where the automatic reload is triggered. So we have to do it
     565    manually::
     566   
     567        sage: shell.run_cell('from sage.misc.attached_files import reload_attached_files_if_modified')
     568        sage: shell.run_cell('reload_attached_files_if_modified()')
     569        ### reloading attached file tmp_....py modified at ... ###
     570
     571        sage: shell.run_cell('a')
     572        3
     573        sage: shell.run_cell('detach({0})'.format(repr(tmp)))
     574        sage: shell.run_cell('attached_files()')
     575        []
     576    """
     577    for filename, mtime in modified_file_iterator():
     578        basename = os.path.basename(filename)
     579        timestr = time.strftime('%T', mtime)
     580        print '### reloading attached file {0} modified at {1} ###'.format(basename, timestr)
     581        code = load_wrap(filename, attach=True)
     582        get_ipython().run_cell(code)
  • new file sage/misc/inputhook.py

    diff --git a/sage/misc/inputhook.py b/sage/misc/inputhook.py
    new file mode 100644
    - +  
     1"""
     2The Sage Input Hook
     3
     4This is a hook into the IPython input prompt and will be called
     5periodically (every 100ms) while Python is sitting idle. We use it to
     6reload attached files if they have changed.
     7"""
     8
     9###########################################################################
     10#       Copyright (C) 2013 Volker Braun <vbraun.name@gmail.com>
     11#
     12#  Distributed under the terms of the GNU General Public License (GPL)
     13#                  http://www.gnu.org/licenses/
     14###########################################################################
     15
     16
     17from sage.misc.attached_files import reload_attached_files_if_modified
     18
     19
     20def sage_inputhook():
     21    """
     22    The input hook.
     23
     24    This function will be called every 100ms when IPython is idle at
     25    the command prompt.
     26   
     27    EXAMPLES::
     28   
     29        sage: from sage.misc.interpreter import get_test_shell
     30        sage: shell = get_test_shell()
     31        sage: tmp = tmp_filename(ext='.py')
     32        sage: f = open(tmp, 'w'); f.write('a = 2\n'); f.close()
     33        sage: shell.run_cell('%attach ' + tmp)
     34        sage: shell.run_cell('a')
     35        2
     36        sage: f = open(tmp, 'w'); f.write('a = 3\n'); f.close()
     37
     38    Note that the doctests are never really at the command prompt, so
     39    we call the input hook manually::
     40   
     41        sage: shell.run_cell('from sage.misc.inputhook import sage_inputhook')
     42        sage: shell.run_cell('sage_inputhook()')
     43        ### reloading attached file tmp_....py modified at ... ###
     44        0
     45
     46        sage: shell.run_cell('a')
     47        3
     48        sage: shell.run_cell('detach({0})'.format(repr(tmp)))
     49        sage: shell.run_cell('attached_files()')
     50        []
     51    """
     52    reload_attached_files_if_modified()
     53    return 0
     54   
     55   
     56       
     57       
     58   
  • sage/misc/interpreter.py

    diff --git a/sage/misc/interpreter.py b/sage/misc/interpreter.py
    a b  
    6868from IPython.utils.py3compat import cast_unicode
    6969from IPython.utils.traitlets import (Integer, CBool, CaselessStrEnum, Enum,
    7070                                     List, Unicode, Instance, Type)
    71 from preparser import (preparse, preparse_file, load_wrap,
    72                        modified_attached_files, attached_files)
     71from preparser import (preparse, preparse_file, load_wrap)
    7372
    7473def embedded():
    7574    """
  • sage/misc/preparser.py

    diff --git a/sage/misc/preparser.py b/sage/misc/preparser.py
    a b  
    213213#  Distributed under the terms of the GNU General Public License (GPL)
    214214#                  http://www.gnu.org/licenses/
    215215###########################################################################
     216
    216217import os, re
     218import base64
    217219
    218220implicit_mul_level = False
    219221numeric_literal_prefix = '_sage_const_'
     
    833835        vars = ','.join(stripped_vars)
    834836
    835837        new_code.append(code[last_end:m.start()])
    836         new_code.append(';%s__tmp__=var("%s"); %s = symbolic_expression(%s).function(%s)' % (ident, vars, func, expr, vars))
     838        new_code.append(';%s__tmp__=var("%s"); %s = symbolic_expression(%s).function(%s)' %
     839                        (ident, vars, func, expr, vars))
    837840        last_end = m.end()
    838841
    839842    if last_end == 0:
     
    983986            pass
    984987        gens_tuple = "(%s,)" % ', '.join(gens)
    985988        new_code.append(code[last_end:m.start()])
    986         new_code.append(";%s%s%s = %s; %s = %s._first_ngens(%s)" % (ident, obj, other_objs, constructor, gens_tuple, obj, len(gens)))
     989        new_code.append(";%s%s%s = %s; %s = %s._first_ngens(%s)" %
     990                        (ident, obj, other_objs, constructor, gens_tuple, obj, len(gens)))
    987991        last_end = m.end()
    988992       
    989993    if last_end == 0:
     
    11371141## Apply the preparser to an entire file
    11381142######################################################
    11391143
    1140 #TODO: This global variable attached should be associtated with an
    1141 #IPython InteractiveShell as opposed to a global variable in this
    1142 #module.
    1143 attached = {}
    1144 
    1145 load_debug_mode = False
    1146 attach_debug_mode = True
    1147 
    1148 def load_attached():
    1149     contents = ""
    1150     for F, tm in attached.iteritems():
    1151         new_tm = os.path.getmtime(F)
    1152         if os.path.exists(F) and new_tm > tm:
    1153             contents += load_wrap(F, attach=True)
    1154     return contents
    1155 
    1156 
    1157 def preparse_file(contents, globals=None, numeric_literals=True, run_attached=True):
     1144def preparse_file(contents, globals=None, numeric_literals=True):
    11581145    """
    11591146    Preparses input, attending to numeric literals and load/attach
    11601147    file directives.
     
    11871174        _sage_const_100 = Integer(100)
    11881175        type(100 ), type(_sage_const_100 )
    11891176    """
    1190     if globals is None: globals = {}
    1191    
    11921177    if not isinstance(contents, basestring):
    1193         raise TypeError, "contents must be a string"
     1178        raise TypeError("contents must be a string")
    11941179
    1195     assert isinstance(contents, basestring)
     1180    if globals is None:
     1181        globals = {}
    11961182
    1197     # Reload attached files that have changed
    1198     if run_attached:
    1199         for F, tm in attached.iteritems():
    1200             new_tm = os.path.getmtime(F)
    1201             if os.path.exists(F) and new_tm > tm:
    1202                 contents = ' "%s"\n'%F + contents
    1203 
    1204    
    12051183    # We keep track of which files have been loaded so far
    12061184    # in order to avoid a recursive load that would result
    12071185    # in creating a massive file and crashing.
     
    14441422        return True
    14451423    return False
    14461424
    1447 
    1448 def reset_load_attach_path():
    1449     """
    1450     Resets the current search path for :func:`load` and
    1451     :meth:`~sage.misc.session.attach`.
    1452 
    1453     The default path is '.' plus any paths specified in the environment
    1454     variable `SAGE_LOAD_ATTACH_PATH`.
    1455 
    1456     EXAMPLES::
    1457 
    1458         sage: load_attach_path()
    1459         ['.']
    1460         sage: t_dir = tmp_dir()
    1461         sage: load_attach_path(t_dir)
    1462         sage: t_dir in load_attach_path()
    1463         True
    1464         sage: reset_load_attach_path(); load_attach_path()
    1465         ['.']
    1466 
    1467     At startup, Sage adds colon-separated paths in the environment
    1468     variable ``SAGE_LOAD_ATTACH_PATH``::
    1469 
    1470         sage: reset_load_attach_path(); load_attach_path()
    1471         ['.']
    1472         sage: os.environ['SAGE_LOAD_ATTACH_PATH'] = '/veni/vidi:vici:'
    1473         sage: reload(sage.misc.preparser)    # Simulate startup
    1474         <module 'sage.misc.preparser' from '...'>
    1475         sage: load_attach_path()
    1476         ['.', '/veni/vidi', 'vici']
    1477         sage: del os.environ['SAGE_LOAD_ATTACH_PATH']
    1478         sage: reload(sage.misc.preparser)    # Simulate startup
    1479         <module 'sage.misc.preparser' from '...'>
    1480         sage: reset_load_attach_path(); load_attach_path()
    1481         ['.']
    1482     """
    1483     global _load_attach_path
    1484     _load_attach_path = ['.']
    1485     if 'SAGE_LOAD_ATTACH_PATH' in os.environ:
    1486         epaths = os.environ['SAGE_LOAD_ATTACH_PATH'].split(':')
    1487         _load_attach_path.extend(epaths)
    1488         while '' in _load_attach_path:
    1489             _load_attach_path.remove('')
    1490 
    1491 # Set up the initial search path for loading and attaching files.  A
    1492 # user can modify the path with the function load_attach_path.
    1493 reset_load_attach_path()
    1494 
    14951425def load_cython(name):
    14961426    import cython
    14971427    cur = os.path.abspath(os.curdir)
     
    16131543    out.write("# -*- coding: utf-8 -*-\n")
    16141544    return contents
    16151545   
    1616 def preparse_file_named_to_stream(name, out, run_attached=True):
     1546def preparse_file_named_to_stream(name, out):
    16171547    r"""
    16181548    Preparse file named \code{name} (presumably a .sage file), outputting to
    16191549    stream \code{out}.
    16201550    """
    16211551    name = os.path.abspath(name)
    1622     dir, _ = os.path.split(name)
    1623     cur = os.path.abspath(os.curdir)
    1624     os.chdir(dir)
    16251552    contents = open(name).read()
    16261553    contents = handle_encoding_declaration(contents, out)
    1627     parsed = preparse_file(contents, run_attached=run_attached)
    1628     os.chdir(cur)
    1629     out.write("# -*- encoding: utf-8 -*-\n")
     1554    parsed = preparse_file(contents)
    16301555    out.write('#'*70+'\n')
    16311556    out.write('# This file was *autogenerated* from the file %s.\n' % name)
    16321557    out.write('#'*70+'\n')
    16331558    out.write(parsed)
    16341559
    1635 def preparse_file_named(name, run_attached=True):
     1560def preparse_file_named(name):
    16361561    r"""
    16371562    Preparse file named \code{name} (presumably a .sage file), outputting to a
    16381563    temporary file.  Returns name of temporary file.
    16391564    """
    1640     import sage.misc.misc
    1641     name = os.path.abspath(name)
    1642     tmpfilename = os.path.abspath(sage.misc.misc.tmp_filename(name) + ".py")
    1643     out = open(tmpfilename,'w')
    1644     preparse_file_named_to_stream(name, out, run_attached=run_attached)
     1565    from sage.misc.misc import tmp_filename
     1566    tmpfilename = tmp_filename(os.path.basename(name)) + '.py'
     1567    out = open(tmpfilename, 'w')
     1568    preparse_file_named_to_stream(name, out)
    16451569    out.close()
    16461570    return tmpfilename
    16471571
     
    17331657        sage: open(t,'w').write("print 'hello world'")
    17341658        sage: sage.misc.preparser.load(t, globals(), attach=True)
    17351659        hello world
    1736         sage: t in sage.misc.preparser.attached
     1660        sage: t in attached_files()
    17371661        True
    17381662
    17391663    You can't attach remote URLs (yet)::
     
    17471671    current working directory, i.e., ``'.'``.  But you can modify the
    17481672    path with :func:`load_attach_path`::
    17491673
    1750         sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
     1674        sage: sage.misc.attached_files.reset(); reset_load_attach_path()
    17511675        sage: load_attach_path()
    17521676        ['.']
    17531677        sage: t_dir = tmp_dir()
     
    17561680        sage: load_attach_path(t_dir)
    17571681        sage: attach('test.py')
    17581682        111
    1759         sage: sage.misc.reset.reset_attached(); reset_load_attach_path() # clean up
     1683        sage: sage.misc.attached_files.reset(); reset_load_attach_path() # clean up
    17601684
    17611685    or by setting the environment variable ``SAGE_LOAD_ATTACH_PATH``
    17621686    to a colon-separated list before starting Sage::
     
    17721696        sage: sage.misc.preparser.load(t, globals())
    17731697        2
    17741698    """
     1699    def exec_file_is(fpath):
     1700        """ To be run before any exec/execfile call """
     1701        if attach:
     1702            from sage.misc.attached_files import add_attached_file
     1703            add_attached_file(fpath)
     1704
    17751705    try:
    17761706        filename = eval(filename, globals)
    17771707    except Exception:
     
    18091739        if not os.path.exists(fpath):
    18101740            raise IOError('did not find file %r to load or attach' % filename)
    18111741    else:
    1812         global _load_attach_path
    1813         for path in _load_attach_path:
     1742        from sage.misc.attached_files import load_attach_path
     1743        for path in load_attach_path():
    18141744            fpath = os.path.join(path, filename)
    18151745            fpath = os.path.expanduser(fpath)
    18161746            if os.path.exists(fpath):
     
    18201750                % filename)
    18211751
    18221752    if fpath.endswith('.py'):
     1753        exec_file_is(fpath)
    18231754        execfile(fpath, globals)
    18241755    elif fpath.endswith('.sage'):
     1756        from sage.misc.attached_files import load_attach_mode
     1757        load_debug_mode, attach_debug_mode = load_attach_mode()
    18251758        if (attach and attach_debug_mode) or ((not attach) and load_debug_mode):
    18261759            # Preparse to a file to enable tracebacks with
    18271760            # code snippets. Use preparse_file_named to make
    18281761            # the file name appear in the traceback as well.
    18291762            # See Trac 11812.
    1830             execfile(preparse_file_named(fpath, run_attached=False), globals)
     1763            exec_file_is(fpath)
     1764            execfile(preparse_file_named(fpath), globals)
    18311765        else:
    18321766            # Preparse in memory only for speed.
    1833             exec(preparse_file(open(fpath).read(), run_attached=False) + "\n", globals)
     1767            exec_file_is(fpath)
     1768            exec preparse_file(open(fpath).read()) + "\n" in globals
    18341769    elif fpath.endswith('.spyx') or fpath.endswith('.pyx'):
    1835         exec(load_cython(fpath), globals)
     1770        exec_file_is(fpath)
     1771        exec load_cython(fpath) in globals
    18361772    elif fpath.endswith('.m'):
    18371773        # Assume magma for now, though maybe .m is used by maple and
    18381774        # mathematica too, and we should really analyze the file
     
    18411777        i = s.find('\n'); s = s[i+1:]
    18421778        print s
    18431779
    1844     if attach:
    1845         # TODO: Use the MD5 hash of the file, instead.
    1846         fpath = os.path.abspath(fpath)
    1847         attached[fpath] = os.path.getmtime(fpath)
    18481780
    1849 
    1850 def load_attach_mode(load_debug=None, attach_debug=None):
    1851     """
    1852     Get or modify the current debug mode for the behavior of
    1853     :func:`load` and :meth:`~sage.misc.session.attach` on ``.sage``
    1854     files.
    1855 
    1856     In debug mode, loaded or attached ``.sage`` files are preparsed
    1857     through a file to make their tracebacks more informative. If not in
    1858     debug mode, then ``.sage`` files are preparsed in memory only for
    1859     performance.
    1860 
    1861     At startup, debug mode is ``True`` for attaching and ``False``
    1862     for loading.
    1863 
    1864     INPUT:
    1865 
    1866     - ``load_debug`` - boolean or (the default) ``None``;
    1867       if not ``None``, then set a new value for the debug mode
    1868       for loading files.
    1869 
    1870     - ``attach_debug`` - boolean or (the default) ``None``;
    1871       same as ``load_debug``, but for attaching files.
    1872 
    1873     OUTPUT:
    1874 
    1875     - if all input values are ``None``, returns a tuple
    1876       giving the current modes for loading and attaching.
    1877 
    1878     EXAMPLES::
    1879 
    1880         sage: load_attach_mode()
    1881         (False, True)
    1882         sage: load_attach_mode(attach_debug=False)
    1883         sage: load_attach_mode()
    1884         (False, False)
    1885         sage: load_attach_mode(load_debug=True)
    1886         sage: load_attach_mode()
    1887         (True, False)
    1888         sage: load_attach_mode(load_debug=False, attach_debug=True)
    1889     """
    1890     global load_debug_mode
    1891     global attach_debug_mode
    1892     if load_debug is None and attach_debug is None:
    1893         return (load_debug_mode, attach_debug_mode)
    1894     if not load_debug is None:
    1895         load_debug_mode = load_debug
    1896     if not attach_debug is None:
    1897         attach_debug_mode = attach_debug
    1898 
    1899 
    1900 def load_attach_path(path=None, replace=False):
    1901     """
    1902     Get or modify the current search path for :func:`load` and
    1903     :meth:`~sage.misc.session.attach`.
    1904 
    1905     INPUT:
    1906 
    1907     - ``path`` - string or list of strings (default: None); path(s) to
    1908       append to or replace the current path
    1909 
    1910     - ``replace`` - boolean (default: False); if ``path`` is not None,
    1911       whether to *replace* the search path instead of *appending* to
    1912       it
    1913 
    1914     OUTPUT:
    1915 
    1916     - None or a *reference* to the current list of search paths
    1917 
    1918     EXAMPLES:
    1919 
    1920     First, we extend the example given in :func:`load`'s docstring::
    1921 
    1922         sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
    1923         sage: load_attach_path()
    1924         ['.']
    1925         sage: t_dir = tmp_dir()
    1926         sage: fullpath = os.path.join(t_dir, 'test.py')
    1927         sage: open(fullpath, 'w').write("print 37 * 3")
    1928         sage: attach('test.py')
    1929         Traceback (most recent call last):
    1930         ...
    1931         IOError: did not find file 'test.py' in load / attach search path
    1932         sage: load_attach_path(t_dir)
    1933         sage: attach('test.py')
    1934         111
    1935         sage: attached_files() == [fullpath]
    1936         True
    1937         sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
    1938         sage: load_attach_path() == ['.']
    1939         True
    1940         sage: load('test.py')
    1941         Traceback (most recent call last):
    1942         ...
    1943         IOError: did not find file 'test.py' in load / attach search path
    1944 
    1945     The function returns a reference to the path list::
    1946 
    1947         sage: reset_load_attach_path(); load_attach_path()
    1948         ['.']
    1949         sage: load_attach_path('/path/to/my/sage/scripts'); load_attach_path()
    1950         ['.', '/path/to/my/sage/scripts']
    1951         sage: load_attach_path(['good', 'bad', 'ugly'], replace=True)
    1952         sage: load_attach_path()
    1953         ['good', 'bad', 'ugly']
    1954         sage: p = load_attach_path(); p.pop()
    1955         'ugly'
    1956         sage: p[0] = 'weird'; load_attach_path()
    1957         ['weird', 'bad']
    1958         sage: reset_load_attach_path(); load_attach_path()
    1959         ['.']
    1960     """
    1961     global _load_attach_path
    1962 
    1963     if path is None:
    1964         return _load_attach_path
    1965     else:
    1966         if isinstance(path, basestring):
    1967             path = [path]
    1968 
    1969         if replace:
    1970             _load_attach_path = path
    1971         else:
    1972             for p in path:
    1973                 if p not in _load_attach_path:
    1974                     _load_attach_path.append(p)
    1975 
    1976 
    1977 import base64   
    19781781def load_wrap(filename, attach=False):
    19791782    """
    19801783    Encodes a load or attach command as valid Python code.
     
    20001803        sage: sage.misc.preparser.base64.b64decode("Zm9vLnNhZ2U=")
    20011804        'foo.sage'
    20021805    """
    2003     return 'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("%s"),globals(),%s)'%(
     1806    return 'sage.misc.preparser.load(sage.misc.preparser.base64.b64decode("{0}"),globals(),{1})'.format(
    20041807        base64.b64encode(filename), attach)
    20051808
    2006 
    2007 def modified_attached_files():
    2008     """
    2009     Returns an iterator over the names of the attached files that have
    2010     changed since last time this function was called.
    2011 
    2012     OUTPUT:
    2013 
    2014     - an iterator over strings
    2015 
    2016     EXAMPLES::
    2017    
    2018         sage: sage.misc.reset.reset_attached()
    2019         sage: t=tmp_filename(ext='.py');
    2020         sage: a = 0
    2021         sage: f = open(t,'w'); f.write("a = 5"); f.close()
    2022         sage: attach(t)
    2023         sage: a
    2024         5
    2025         sage: len(list(sage.misc.preparser.modified_attached_files()))
    2026         0
    2027         sage: import time; time.sleep(2)
    2028         sage: f = open(t,'w'); f.write("a = 10"); f.close()
    2029         sage: len(list(sage.misc.preparser.modified_attached_files()))
    2030         1
    2031         sage: len(list(sage.misc.preparser.modified_attached_files()))
    2032         0
    2033     """
    2034     # A generator is the perfect data structure here, since something
    2035     # could go wrong loading one file, and we end up only marking the
    2036     # ones that we returned as loaded.
    2037     for F in attached.keys():
    2038         tm = attached[F]
    2039         new_tm = os.path.getmtime(F)
    2040         if os.path.exists(F) and new_tm > tm:
    2041             # F is newer than last time we loaded it.
    2042             attached[F] = os.path.getmtime(F)
    2043             yield F
    2044    
    2045 def attached_files():
    2046     """
    2047     Returns a list of all files attached to the current session with
    2048     :meth:`~sage.misc.session.attach`.
    2049 
    2050     OUTPUT:
    2051 
    2052     - a sorted list of strings; the filenames
    2053 
    2054     EXAMPLES::
    2055 
    2056         sage: sage.misc.reset.reset_attached()
    2057         sage: t = tmp_filename(ext='.py')
    2058         sage: open(t,'w').write("print 'hello world'")
    2059         sage: attach(t)
    2060         hello world
    2061         sage: attached_files() == [t]
    2062         True
    2063     """
    2064     return list(sorted(attached.keys()))
    2065 
    2066 def detach(filename):
    2067     """
    2068     Detaches a file, if it was attached with
    2069     :meth:`~sage.misc.session.attach`.
    2070 
    2071     INPUT:
    2072 
    2073     - ``filename`` - a string, or a list of strings, or a tuple of strings
    2074 
    2075     EXAMPLES::
    2076 
    2077         sage: sage.misc.reset.reset_attached()
    2078         sage: t = tmp_filename(ext='.py')
    2079         sage: open(t,'w').write("print 'hello world'")
    2080         sage: attach(t)
    2081         hello world
    2082         sage: attached_files() == [t]
    2083         True
    2084         sage: detach(t)
    2085         sage: attached_files()
    2086         []
    2087    
    2088     ::
    2089    
    2090         sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
    2091         sage: load_attach_path()
    2092         ['.']
    2093         sage: t_dir = tmp_dir()
    2094         sage: fullpath = os.path.join(t_dir, 'test.py')
    2095         sage: open(fullpath, 'w').write("print 37 * 3")
    2096         sage: load_attach_path(t_dir)
    2097         sage: attach('test.py')
    2098         111
    2099         sage: attached_files() == [os.path.normpath(fullpath)]
    2100         True
    2101         sage: detach('test.py')
    2102         sage: attached_files()
    2103         []
    2104         sage: attach('test.py')
    2105         111
    2106         sage: fullpath = os.path.join(t_dir, 'test2.py')
    2107         sage: open(fullpath, 'w').write("print 3")
    2108         sage: attach('test2.py')
    2109         3
    2110         sage: detach(attached_files())
    2111         sage: attached_files()
    2112         []
    2113         sage: sage.misc.reset.reset_attached(); reset_load_attach_path() # clean up
    2114 
    2115     TESTS::
    2116 
    2117         sage: detach('/tmp/a b/a.sage')
    2118         Traceback (most recent call last):
    2119         ...
    2120         ValueError: File '/tmp/a b/a.sage' seems not to be attached. To see a list of attached files, call the function attached_files
    2121     """
    2122     if isinstance(filename, basestring):
    2123         filelist = [filename]
    2124     else:
    2125         filelist = [str(x) for x in filename]
    2126 
    2127     for filename in filelist:
    2128         fpath = os.path.expanduser(filename)
    2129         if not os.path.isabs(fpath):
    2130             for path in load_attach_path():
    2131                 epath = os.path.expanduser(path)
    2132                 fpath = os.path.join(epath, filename)
    2133                 fpath = os.path.abspath(fpath)
    2134                 if fpath in attached:
    2135                     break
    2136  
    2137         if fpath in attached:
    2138             attached.pop(fpath)
    2139         else:
    2140             raise ValueError("File '{0}' seems not to be attached. To see a "
    2141                              "list of attached files, call the function "
    2142                              "attached_files".format(filename))
  • sage/misc/reset.pyx

    diff --git a/sage/misc/reset.pyx b/sage/misc/reset.pyx
    a b  
    7777    forget()
    7878    reset_interfaces()
    7979    if attached:
    80         reset_attached()
     80        import sage.misc.attached_files
     81        sage.misc.attached_files.reset()
    8182
    8283def restore(vars=None):
    8384    """
     
    159160def reset_interfaces():
    160161    from sage.interfaces.quit import expect_quitall
    161162    expect_quitall()
    162    
    163 def reset_attached():
    164     """
    165     Remove all the attached files from the list of attached files.
    166 
    167     EXAMPLES::
    168    
    169         sage: sage.misc.reset.reset_attached()
    170         sage: t = tmp_filename(ext='.py')
    171         sage: open(t,'w').write("print 'hello world'")
    172         sage: attach(t)
    173         hello world
    174         sage: attached_files() == [t]
    175         True
    176         sage: sage.misc.reset.reset_attached()
    177         sage: attached_files()
    178         []
    179     """
    180     import preparser
    181     preparser.attached = {}
  • sage/misc/sage_extension.py

    diff --git a/sage/misc/sage_extension.py b/sage/misc/sage_extension.py
    a b  
    9898            sage: shell.run_cell('%attach ' + tmp)
    9999            sage: shell.run_cell('a')
    100100            2
    101             sage: import time; time.sleep(1)
    102101            sage: f = open(tmp, 'w'); f.write('a = 3\n'); f.close()
     102
     103        Note that the doctests are never really at the command prompt, so
     104        we call the input hook manually::
     105   
     106            sage: shell.run_cell('from sage.misc.inputhook import sage_inputhook')
     107            sage: shell.run_cell('sage_inputhook()')
     108            ### reloading attached file run_cell.py modified at ... ###
     109            0
     110
    103111            sage: shell.run_cell('a')
    104112            3
    105113            sage: shell.run_cell('detach(%r)'%tmp)
     
    110118        from sage.misc.preparser import load_wrap
    111119        return self.shell.ex(load_wrap(s, attach=True))
    112120
    113     def pre_run_code_hook(self, ip):
    114         """
    115         Load the attached files (if needed) before running some code.
    116 
    117         The examples are from the %attach magic.
    118 
    119         EXAMPLES::
    120 
    121             sage: import os
    122             sage: from sage.misc.interpreter import get_test_shell
    123             sage: shell = get_test_shell()
    124             sage: tmp = os.path.normpath(os.path.join(SAGE_TMP, 'run_cell.py'))
    125             sage: f = open(tmp, 'w'); f.write('a = 2\n'); f.close()
    126             sage: shell.run_cell('%attach ' + tmp)
    127             sage: shell.run_cell('a')
    128             2
    129             sage: import time; time.sleep(1)
    130             sage: f = open(tmp, 'w'); f.write('a = 3\n'); f.close()
    131             sage: shell.run_cell('a')
    132             3
    133             sage: shell.run_cell('detach(%r)'%tmp)
    134             sage: shell.run_cell('attached_files()')
    135             []
    136             sage: os.remove(tmp)
    137         """
    138         from sage.misc.preparser import load_attached
    139         s=load_attached()
    140         if s:
    141             self.shell.ex(s)
    142         raise TryNext
    143 
    144121    @line_magic
    145122    def iload(self, s):
    146123        """
     
    417394        self.shell = shell
    418395        self.auto_magics = SageMagics(shell)
    419396        shell.register_magics(self.auto_magics)
    420         shell.set_hook('pre_run_code_hook', self.auto_magics.pre_run_code_hook)
    421397        displayhook.SPTextFormatter = displayhook.SagePlainTextFormatter(config=shell.config)
    422398        shell.display_formatter.formatters['text/plain'] = displayhook.SPTextFormatter
    423399        from sage.misc.edit_module import edit_devel
     
    425401        self.init_inspector()
    426402        self.init_line_transforms()
    427403        self.register_interface_magics()
     404        from sage.misc.inputhook import sage_inputhook
     405        from IPython.lib import inputhook
     406        inputhook.set_inputhook(sage_inputhook)
    428407
    429408        # right now, the shutdown hook calling quit_sage() doesn't
    430409        # work when we run doctests that involve creating test shells.
  • sage/misc/session.pyx

    diff --git a/sage/misc/session.pyx b/sage/misc/session.pyx
    a b  
    372372    for k, x in D.items():
    373373        state[k] = x
    374374
    375 
    376 def attach(*files):
    377     """
    378     Attach a file or files to a running instance of Sage and also load
    379     that file.
    380 
    381     USAGE:
    382 
    383     ``attach file1 ...`` - space-separated list of ``.py``, ``.pyx``,
    384     and ``.sage`` files, or ``attach('file1', 'file2')`` - filenames as
    385     strings, given as arguments to :func:`attach`.
    386 
    387     :meth:`~sage.misc.preparser.load` is the same as :func:`attach`, but
    388     doesn't automatically reload a file when it changes.
    389 
    390     .. NOTE::
    391 
    392        In addition to ``attach('foo.sage')``, from the sage prompt
    393        you can also just type ``attach "foo.sage"``.  However this
    394        second common usage is not part of the Python language.
    395 
    396     EFFECT:
    397 
    398     Each file is read in and added to an internal list of watched files.
    399     The meaning of reading in a file depends on the file type:
    400 
    401     -  ``.py`` files are read in with no preparsing (so, e.g., ``2^3`` is 2
    402        bit-xor 3);
    403 
    404     -  ``.sage`` files are preparsed, then the result is read in;
    405 
    406     - ``.pyx`` files are *not* preparsed, but rather are compiled to a
    407        module ``m`` and then ``from m import *`` is executed.
    408 
    409     The contents of the file are then loaded, which means they are read
    410     into the running Sage session. For example, if ``foo.sage`` contains
    411     ``x=5``, after attaching ``foo.sage`` the variable ``x`` will be set
    412     to 5. Moreover, any time you change ``foo.sage``, before you execute
    413     a command, the attached file will be re-read automatically (with no
    414     intervention on your part).
    415 
    416     EXAMPLES:
    417 
    418     You attach a file, e.g., ``foo.sage`` or ``foo.py`` or
    419     ``foo.pyx``, to a running Sage session by typing::
    420 
    421         sage: attach foo.sage   # or foo.py or foo.pyx or even a URL to such a file (not tested)
    422 
    423     or::
    424 
    425         sage: attach('foo.sage')  # not tested
    426 
    427     Here we test attaching multiple files at once::
    428 
    429         sage: sage.misc.reset.reset_attached()
    430         sage: t1 = tmp_filename(ext='.py')
    431         sage: open(t1,'w').write("print 'hello world'")
    432         sage: t2 = tmp_filename(ext='.py')
    433         sage: open(t2,'w').write("print 'hi there xxx'")
    434         sage: attach(t1, t2)
    435         hello world
    436         hi there xxx
    437         sage: set(attached_files()) == set([t1,t2])
    438         True
    439 
    440     .. SEEALSO::
    441 
    442         - :meth:`~sage.misc.preparser.attached_files` returns a list of
    443           all currently attached files.
    444 
    445         - :meth:`~sage.misc.preparser.detach` instructs Sage to remove a
    446           file from the internal list of watched files.
    447 
    448         - :meth:`~sage.misc.preparser.load_attach_path` allows you to
    449           get or modify the current search path for loading and attaching
    450           files.
    451     """
    452     import preparser
    453     for filename in files:
    454         preparser.load(filename, globals(), attach=True)