Ticket #378: trac_387-load_attach_path.2.1.patch

File trac_387-load_attach_path.2.1.patch, 14.0 KB (added by flawrence, 11 years ago)

use os.path.userexpand on load, separate reset function, also use paths for detach. actually replaces previous this time..

  • sage/misc/all.py

    # HG changeset patch
    # User Mitesh Patel <qed777@gmail.com>
    # Date 1266709537 28800
    # Node ID 6e89162cca218572b8fcef451fc29c2ecf626fa7
    # Parent  120c07be6358d93bcff503363d379c26b8342f2b
    #378: Configurable load + attach path
    
    diff -r 120c07be6358 -r 6e89162cca21 sage/misc/all.py
    a b  
    5858
    5959from defaults import set_default_variable_name
    6060
    61 from preparser import preparse, implicit_multiplication, BackslashOperator, attached_files, detach
     61from preparser import preparse, implicit_multiplication, BackslashOperator, attached_files, detach, load_attach_path, reset_load_attach_path
    6262
    6363from interpreter import preparser
    6464
  • sage/misc/preparser.py

    diff -r 120c07be6358 -r 6e89162cca21 sage/misc/preparser.py
    a b  
    13631363def is_loadable_filename(filename):
    13641364    """
    13651365    Returns whether a file can be loaded into Sage.  This checks only
    1366     whether its name ends in one of the supported extensions .py,
    1367     .pyx, .sage, and .spyx.
     1366    whether its name ends in one of the supported extensions ``.py``,
     1367    ``.pyx``, ``.sage``, ``.spyx``, and ``.m``.  Note: :func:`load`
     1368    assumes the latter signifies a Magma file.
    13681369
    13691370    INPUT:
    13701371
     
    13821383        False
    13831384        sage: sage.misc.preparser.is_loadable_filename('foo.sage')
    13841385        True
     1386        sage: sage.misc.preparser.is_loadable_filename('foo.m')
     1387        True
    13851388    """
    1386     # Loop over list of supported code file extensions for the load function.   
    1387     for ext in ['.py', '.pyx', '.sage', '.spyx']:
    1388         if filename.endswith(ext):
    1389             return True
     1389    if filename.endswith(('.py', '.pyx', '.sage', '.spyx', '.m')):
     1390        return True
    13901391    return False
    13911392
     1393
     1394def reset_load_attach_path():
     1395    """
     1396    Resets the current search path for loading and attaching files.
     1397   
     1398    The default path is '.' plus any paths specified in the environment
     1399    variable `SAGE_LOAD_ATTACH_PATH`.
     1400   
     1401    EXAMPLES::
     1402   
     1403        sage: load_attach_path()
     1404        ['.']
     1405        sage: t_dir = tmp_dir()
     1406        sage: load_attach_path(t_dir)
     1407        sage: t_dir in load_attach_path()
     1408        True
     1409        sage: reset_load_attach_path(); load_attach_path()
     1410        ['.']
     1411   
     1412    At startup, Sage adds colon-separated paths in the environment
     1413    variable ``SAGE_LOAD_ATTACH_PATH``::
     1414
     1415        sage: reset_load_attach_path(); load_attach_path()
     1416        ['.']
     1417        sage: os.environ['SAGE_LOAD_ATTACH_PATH'] = '/veni/vidi:vici:'
     1418        sage: reload(sage.misc.preparser)    # Simulate startup
     1419        <module 'sage.misc.preparser' from '...'>
     1420        sage: load_attach_path()
     1421        ['.', '/veni/vidi', 'vici']
     1422        sage: del os.environ['SAGE_LOAD_ATTACH_PATH']
     1423        sage: reload(sage.misc.preparser)    # Simulate startup
     1424        <module 'sage.misc.preparser' from '...'>
     1425        sage: reset_load_attach_path(); load_attach_path()
     1426        ['.']
     1427    """
     1428    global _load_attach_path
     1429    _load_attach_path = ['.']
     1430    if 'SAGE_LOAD_ATTACH_PATH' in os.environ:
     1431        epaths = os.environ['SAGE_LOAD_ATTACH_PATH'].split(':')
     1432        _load_attach_path.extend(epaths)
     1433        while '' in _load_attach_path:
     1434            _load_attach_path.remove('')
     1435
     1436# Set up the initial search path for loading and attaching files.  A
     1437# user can modify the path with the function load_attach_path.
     1438reset_load_attach_path()
     1439
    13921440def load(filename, globals, attach=False):
    13931441    """
    13941442    Executes a file in the scope given by ``globals``.  The
    13951443    ``filename`` itself is also evaluated in the scope.  If the name
    1396     starts with http://, it is treated as a URL and downloaded.
     1444    starts with ``http://``, it is treated as a URL and downloaded.
    13971445
    13981446    .. note:: For Cython files, the situation is more complicated --
    1399        the module is first compiled to a temporary module t and
     1447       the module is first compiled to a temporary module ``t`` and
    14001448       executed via::
    14011449
    14021450           from t import *
     
    14451493        sage: sage.misc.preparser.load('a.foo',globals())
    14461494        Traceback (most recent call last):
    14471495        ...
    1448         ValueError: argument (='a.foo') to load or attach must have extension py, sage, or pyx
     1496        ValueError: argument (='a.foo') to load or attach must have extension py, pyx, sage, spyx, or m
    14491497
    14501498    A filename given as an expression get evaluated.  This ensures
    14511499    that ``load DATA+'foo.sage'`` works in the Notebook, say::
     
    14641512        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
    14651513        sage: sage.misc.preparser.load(t, globals(), attach=True)
    14661514        hello world
    1467         sage: t in sage.misc.preparser.attached
     1515        sage: os.path.normpath(t) in sage.misc.preparser.attached
    14681516        True
    14691517
    14701518    You can't attach remote URLs (yet)::
     
    14731521        Traceback (most recent call last):
    14741522        ...
    14751523        NotImplementedError: you can't attach a URL
     1524
     1525    The default search path for loading and attaching files is the
     1526    current working directory, i.e., ``'.'``.  But you can modify the
     1527    path with :func:`load_attach_path`::
     1528
     1529        sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
     1530        sage: load_attach_path()
     1531        ['.']
     1532        sage: t_dir = tmp_dir()
     1533        sage: fullpath = os.path.join(t_dir, 'test.py')
     1534        sage: open(fullpath, 'w').write("print 37 * 3")
     1535        sage: load_attach_path(t_dir)
     1536        sage: attach('test.py')
     1537        111
     1538        sage: sage.misc.reset.reset_attached(); reset_load_attach_path() # clean up
     1539
     1540    or by setting the environment variable ``SAGE_LOAD_ATTACH_PATH``
     1541    to a colon-separated list before starting Sage::
     1542
     1543        $ export SAGE_LOAD_ATTACH_PATH="/path/to/my/library:/path/to/utils"
     1544        $ sage
     1545        sage: load_attach_path()          # not tested
     1546        ['.', '/path/to/my/library', '/path/to/utils']
    14761547    """
    14771548    try:
    14781549        filename = eval(filename, globals)
     
    14851556            for file in v:
    14861557                load(file, globals, attach=attach)
    14871558            return
    1488        
     1559
    14891560    filename = filename.strip()
    14901561   
    14911562    if filename.lower().startswith('http://'):
     
    14931564            # But see http://en.wikipedia.org/wiki/HTTP_ETag for how
    14941565            # we will do this.
    14951566            # http://www.diveintopython.org/http_web_services/etags.html
    1496             raise NotImplementedError, "you can't attach a URL"
     1567            raise NotImplementedError("you can't attach a URL")
    14971568        from remote_file import get_remote_file
    14981569        filename = get_remote_file(filename, verbose=False)
    14991570
    1500     if filename.endswith('.py'):
    1501         execfile(filename, globals)
    1502     elif filename.endswith('.sage'):
    1503         exec(preparse_file(open(filename).read()), globals)
    1504     elif filename.endswith('.spyx') or filename.endswith('.pyx'):
     1571    if not is_loadable_filename(filename):
     1572        raise ValueError('argument (=%r) to load or attach must have extension py, pyx, sage, spyx, or m' % filename)
     1573
     1574    fpath = os.path.expanduser(filename)
     1575    if os.path.isabs(fpath):
     1576        if not os.path.exists(fpath):
     1577            raise IOError('did not find file %r to load or attach' % filename)
     1578    else:
     1579        global _load_attach_path
     1580        for path in _load_attach_path:
     1581            fpath = os.path.join(path, filename)
     1582            fpath = os.path.expanduser(fpath)
     1583            if os.path.exists(fpath):
     1584                break
     1585        else:
     1586            raise IOError('did not find file %r in load / attach search path' \
     1587                % filename)
     1588
     1589    if fpath.endswith('.py'):
     1590        execfile(fpath, globals)
     1591    elif fpath.endswith('.sage'):
     1592        exec(preparse_file(open(fpath).read()), globals)
     1593    elif fpath.endswith('.spyx') or fpath.endswith('.pyx'):
    15051594        import interpreter
    1506         exec(interpreter.load_cython(filename), globals)
    1507     elif filename.endswith('.m'):
     1595        exec(interpreter.load_cython(fpath), globals)
     1596    elif fpath.endswith('.m'):
    15081597        # Assume magma for now, though maybe .m is used by maple and
    15091598        # mathematica too, and we should really analyze the file
    15101599        # further.
    1511         s = globals['magma'].load(filename)
     1600        s = globals['magma'].load(fpath)
    15121601        i = s.find('\n'); s = s[i+1:]
    15131602        print s
     1603
     1604    if attach:
     1605        # TODO: Use the MD5 hash of the file, instead.
     1606        fpath = os.path.abspath(fpath)
     1607        attached[fpath] = os.path.getmtime(fpath)
     1608
     1609
     1610def load_attach_path(path=None, replace=False):
     1611    """
     1612    Get or modify the current search path for loading and attaching
     1613    files.
     1614
     1615    INPUT:
     1616
     1617    - ``path`` - string or list of strings (default: None); path(s) to
     1618      append to or replace the current path
     1619
     1620    - ``replace`` - boolean (default: False); if ``path`` is not None,
     1621      whether to *replace* the search path instead of *appending* to
     1622      it
     1623
     1624    OUTPUT:
     1625
     1626    - None or a *reference* to the current list of search paths
     1627
     1628    EXAMPLES:
     1629
     1630    First, we extend the example given in :func:`load`'s docstring::
     1631
     1632        sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
     1633        sage: load_attach_path()
     1634        ['.']
     1635        sage: t_dir = tmp_dir()
     1636        sage: fullpath = os.path.join(t_dir, 'test.py')
     1637        sage: open(fullpath, 'w').write("print 37 * 3")
     1638        sage: attach('test.py')
     1639        Traceback (most recent call last):
     1640        ...
     1641        IOError: did not find file 'test.py' in load / attach search path
     1642        sage: load_attach_path(t_dir)
     1643        sage: attach('test.py')
     1644        111
     1645        sage: attached_files() == [fullpath]
     1646        True       
     1647        sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
     1648        sage: load_attach_path() == ['.']
     1649        True
     1650        sage: load('test.py')
     1651        Traceback (most recent call last):
     1652        ...
     1653        IOError: did not find file 'test.py' in load / attach search path
     1654
     1655    The function returns a reference to the path list::
     1656
     1657        sage: reset_load_attach_path(); load_attach_path()
     1658        ['.']
     1659        sage: load_attach_path('/path/to/my/sage/scripts'); load_attach_path()
     1660        ['.', '/path/to/my/sage/scripts']
     1661        sage: load_attach_path(['good', 'bad', 'ugly'], replace=True)
     1662        sage: load_attach_path()
     1663        ['good', 'bad', 'ugly']
     1664        sage: p = load_attach_path(); p.pop()
     1665        'ugly'
     1666        sage: p[0] = 'weird'; load_attach_path()
     1667        ['weird', 'bad']
     1668        sage: reset_load_attach_path(); load_attach_path()
     1669        ['.']
     1670    """
     1671    global _load_attach_path
     1672
     1673    if path is None:
     1674        return _load_attach_path
    15141675    else:
    1515         raise ValueError, "argument (='%s') to load or attach must have extension py, sage, or pyx"%filename
     1676        if isinstance(path, basestring):
     1677            path = [path]
    15161678
    1517     if attach and os.path.exists(filename):
    1518         attached[filename] = os.path.getmtime(filename)
     1679        if replace:
     1680            _load_attach_path = path
     1681        else:
     1682            for p in path:
     1683                if p not in _load_attach_path:
     1684                    _load_attach_path.append(p)
    15191685
    15201686
    15211687import base64   
     
    15981764
    15991765        sage: sage.misc.reset.reset_attached()
    16001766        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1767        sage: t = os.path.normpath(t)
    16011768        sage: attach(t)
    16021769        hello world
    16031770        sage: attached_files() == [t]
    16041771        True
    1605    
    16061772    """
    16071773    return list(sorted(attached.keys()))
    16081774
     
    16181784
    16191785        sage: sage.misc.reset.reset_attached()
    16201786        sage: t=tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     1787        sage: t = os.path.normpath(t)
    16211788        sage: attach(t)
    16221789        hello world
    16231790        sage: attached_files() == [t]
     
    16251792        sage: detach(t)
    16261793        sage: attached_files()
    16271794        []
     1795   
     1796    ::
     1797   
     1798        sage: sage.misc.reset.reset_attached(); reset_load_attach_path()
     1799        sage: load_attach_path()
     1800        ['.']
     1801        sage: t_dir = tmp_dir()
     1802        sage: fullpath = os.path.join(t_dir, 'test.py')
     1803        sage: open(fullpath, 'w').write("print 37 * 3")
     1804        sage: load_attach_path(t_dir)
     1805        sage: attach('test.py')
     1806        111
     1807        sage: attached_files() == [os.path.normpath(fullpath)]
     1808        True
     1809        sage: detach('test.py')
     1810        sage: attached_files()
     1811        []
     1812        sage: attach('test.py')
     1813        111
     1814        sage: detach(fullpath)
     1815        sage: attached_files()
     1816        []
     1817        sage: sage.misc.reset.reset_attached(); reset_load_attach_path() # clean up
     1818       
    16281819    """
    1629     if attached.has_key(filename):
    1630         del attached[filename]
     1820    fpath = os.path.expanduser(filename)
     1821    if not os.path.isabs(fpath):
     1822        for path in load_attach_path():
     1823            epath = os.path.expanduser(path)
     1824            fpath = os.path.join(epath, filename)
     1825            if fpath in attached:
     1826                break
     1827   
     1828    if fpath in attached:
     1829        attached.pop(fpath)
     1830    else:
     1831        raise ValueError("File '%r' seems not to be attached. To see a list \
     1832            of attached files, call attached_files()" % filename)
  • sage/misc/reset.pyx

    diff -r 120c07be6358 -r 6e89162cca21 sage/misc/reset.pyx
    a b  
    128128   
    129129        sage: sage.misc.reset.reset_attached()
    130130        sage: t = tmp_filename()+'.py'; open(t,'w').write("print 'hello world'")
     131        sage: t = os.path.normpath(t)
    131132        sage: attach(t)
    132133        hello world
    133134        sage: attached_files() == [t]
  • sage/misc/session.pyx

    diff -r 120c07be6358 -r 6e89162cca21 sage/misc/session.pyx
    a b  
    380380        sage: sage.misc.reset.reset_attached()
    381381        sage: t1=tmp_filename()+'.py'; open(t1,'w').write("print 'hello world'")
    382382        sage: t2=tmp_filename()+'.py'; open(t2,'w').write("print 'hi there xxx'")
     383        sage: t1, t2 = map(os.path.normpath, (t1, t2))
    383384        sage: attach(t1, t2)
    384385        hello world
    385386        hi there xxx