Ticket #7502: 7502-lazy-import.2.patch

File 7502-lazy-import.2.patch, 7.8 KB (added by mpatel, 12 years ago)

Also map tab completion. Add to reference manual. Rebased vs. 4.3.2.alpha0. Replaces previous.

  • doc/en/reference/misc.rst

    # HG changeset patch
    # User Robert Bradshaw <robertwb@math.washington.edu>
    # Date 1258708100 28800
    # Node ID 4d071688baf0aa5454fef80cbcb46fef33aac75c
    # Parent  161cb2dfea787a27a96fe67901ade5c13a59d1de
    Lazy import.
    
    diff --git a/doc/en/reference/misc.rst b/doc/en/reference/misc.rst
    a b Miscellaneous 
    2121   sage/misc/latex
    2222   sage/misc/latex_macros
    2323   sage/misc/lazy_attribute
     24   sage/misc/lazy_import
    2425   sage/misc/log
    2526   sage/misc/persist
    2627   sage/misc/func_persist
  • new file sage/misc/lazy_import.py

    diff --git a/sage/misc/lazy_import.py b/sage/misc/lazy_import.py
    new file mode 100644
    - +  
     1r"""
     2Lazy imports
     3
     4This module allows one to lazily import callable objects into the
     5global namespace, where the actual import is delayed until the object
     6is actually called or inspected. This is useful for modules that are
     7expensive to import or may cause circular references, though there is
     8some overhead in its use.
     9
     10EXAMPLES::
     11
     12    sage: from sage.misc.lazy_import import lazy_import
     13    sage: lazy_import('sage.rings.all', 'ZZ')
     14    sage: type(ZZ)
     15    <class 'sage.misc.lazy_import.LazyImport'>
     16    sage: ZZ(4.0)
     17    4
     18
     19AUTHOR:
     20
     21 - Robert Bradshaw
     22"""
     23
     24#*****************************************************************************
     25#       Copyright (C) 2009 Robert Bradshaw <robertwb@math.washington.edu>
     26#
     27#  Distributed under the terms of the GNU General Public License (GPL)
     28#
     29#    This code is distributed in the hope that it will be useful,
     30#    but WITHOUT ANY WARRANTY; without even the implied warranty of
     31#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     32#    General Public License for more details.
     33#
     34#  The full text of the GPL is available at:
     35#
     36#                  http://www.gnu.org/licenses/
     37#*****************************************************************************
     38
     39import inspect
     40import sageinspect
     41
     42class LazyImport(object):
     43    """
     44    EXAMPLES::
     45   
     46        sage: from sage.misc.lazy_import import LazyImport
     47        sage: my_integer = LazyImport('sage.rings.all', 'Integer')
     48        sage: my_integer(4)
     49        4
     50        sage: my_integer('101', base=2)
     51        5
     52        sage: my_integer(3/2)
     53        Traceback (most recent call last):
     54        ...
     55        TypeError: no conversion of this rational to integer
     56    """
     57    def __init__(self, module, name):
     58        """
     59        EXAMPLES::
     60       
     61            sage: from sage.misc.lazy_import import LazyImport
     62            sage: my_isprime = LazyImport('sage.all', 'is_prime')
     63            sage: my_isprime(5)
     64            True
     65            sage: my_isprime(55)
     66            False
     67        """
     68        self._module = module
     69        self._name = name
     70        self._object = None
     71   
     72    def _get_object(self):
     73        """
     74        Return the wrapped object, importing it if necessary.
     75       
     76        EXAMPLE::
     77       
     78            sage: from sage.misc.lazy_import import LazyImport
     79            sage: my_integer_ring = LazyImport('sage.rings.all', 'ZZ')
     80            sage: my_integer_ring._object is None
     81            True
     82            sage: my_integer_ring._get_object()
     83            Integer Ring
     84            sage: my_integer_ring._object is None
     85            False
     86        """
     87        if self._object is None:
     88            self._object = getattr(__import__(self._module, {}, {}, [self._name]), self._name)
     89        return self._object
     90   
     91    def _sage_doc_(self):
     92        """
     93        Return the docstring of the wrapped object for introspection.
     94       
     95        EXAMPLES::
     96       
     97            sage: from sage.misc.lazy_import import LazyImport
     98            sage: my_isprime = LazyImport('sage.all', 'is_prime')
     99            sage: my_isprime._sage_doc_() is is_prime.__doc__
     100            True
     101        """
     102        return sageinspect.sage_getdoc(self._get_object())
     103   
     104    def _sage_src_(self):
     105        """
     106        Returns the source of the wrapped object for introspection.
     107       
     108        EXAMPLES::
     109
     110            sage: from sage.misc.lazy_import import LazyImport
     111            sage: my_isprime = LazyImport('sage.all', 'is_prime')
     112            sage: 'def is_prime(' in my_isprime._sage_src_()
     113            True
     114        """
     115        return sageinspect.sage_getsource(self._get_object())
     116   
     117    def _sage_argspec_(self):
     118        """
     119        Returns the argspec of the wrapped object for introspection.
     120       
     121        EXAMPLES::
     122       
     123            sage: from sage.misc.lazy_import import LazyImport
     124            sage: rm = LazyImport('sage.all', 'random_matrix')
     125            sage: rm._sage_argspec_()
     126            (['R', 'nrows', 'ncols', 'sparse', 'density'],
     127             'args',
     128             'kwds',
     129             (None, False, 1))
     130        """
     131        return sageinspect.sage_getargspec(self._get_object())
     132   
     133    def __getattr__(self, attr):
     134        """
     135        Attribute lookup on self defers to attribute lookup on the
     136        wrapped object.
     137       
     138        EXAMPLES::
     139           
     140            sage: from sage.misc.lazy_import import LazyImport
     141            sage: my_integer = LazyImport('sage.rings.all', 'Integer')
     142            sage: my_integer.sqrt is Integer.sqrt
     143            True
     144        """
     145        return getattr(self._get_object(), attr)
     146   
     147    def __dir__(self):
     148        """
     149        Tab completion on self defers to completion on the wrapped
     150        object.
     151
     152        EXAMPLES::
     153
     154            sage: from sage.misc.lazy_import import LazyImport
     155            sage: my_ZZ = LazyImport('sage.rings.all', 'ZZ')
     156            sage: dir(my_ZZ) == dir(ZZ)
     157            True
     158        """
     159        return dir(self._get_object())
     160
     161    def __call__(self, *args, **kwds):
     162        """
     163        Calling self calls the wrapped object.
     164       
     165        EXAMPLES::
     166       
     167            sage: from sage.misc.lazy_import import LazyImport
     168            sage: my_isprime = LazyImport('sage.all', 'is_prime')
     169            sage: my_isprime(12)
     170            False
     171            sage: my_isprime(13)
     172            True
     173        """
     174        return self._get_object()(*args, **kwds)
     175
     176def lazy_import(module, names, _as=None):
     177    """
     178    Create a lazy import object and inject it into the caller's global
     179    namespace. For the purposes of introspection and calling, this
     180    like performing a lazy "from module import name" where the import
     181    is delayed until the object actually is used or inspected.
     182   
     183    EXAMPLES::
     184   
     185        sage: from sage.misc.lazy_import import lazy_import
     186        sage: lazy_import('sage.rings.all', 'ZZ')
     187        sage: type(ZZ)
     188        <class 'sage.misc.lazy_import.LazyImport'>
     189        sage: ZZ(4.0)
     190        4
     191        sage: lazy_import('sage.rings.all', 'RDF', 'my_RDF')
     192        sage: my_RDF(1/2)
     193        0.5
     194        sage: my_RDF._get_object() is RDF
     195        True
     196       
     197        sage: lazy_import('sage.all', ['QQ', 'RR'], ['my_QQ', 'my_RR'])
     198        sage: my_QQ._get_object() is QQ
     199        True
     200        sage: my_RR._get_object() is RR
     201        True
     202    """
     203    if _as is None:
     204        _as = names
     205    if isinstance(names, str):
     206        names = [names]
     207        _as = [_as]
     208    calling_globals = inspect.currentframe().f_back.f_globals
     209    for name, alias in zip(names, _as):
     210        if alias is None:
     211            alias = name
     212        calling_globals[alias] = LazyImport(module, name)
  • sage/misc/sageinspect.py

    diff --git a/sage/misc/sageinspect.py b/sage/misc/sageinspect.py
    a b def sage_getargspec(obj): 
    362362        return _sage_getargspec_cython(source)
    363363    if not callable(obj):
    364364        raise TypeError, "obj is not a code object"
     365    if hasattr(obj, '_sage_argspec_'):
     366        return obj._sage_argspec_()
    365367    if inspect.isfunction(obj):
    366368        func_obj = obj
    367369    elif inspect.ismethod(obj):