Lazy imports¶
This module allows one to lazily import objects into a namespace, where the actual import is delayed until the object is actually called or inspected. This is useful for modules that are expensive to import or may cause circular references, though there is some overhead in its use.
EXAMPLES:
sage: lazy_import('sage.rings.integer_ring', 'ZZ')
sage: type(ZZ)
<class 'sage.misc.lazy_import.LazyImport'>
sage: ZZ(4.0)
4
>>> from sage.all import *
>>> lazy_import('sage.rings.integer_ring', 'ZZ')
>>> type(ZZ)
<class 'sage.misc.lazy_import.LazyImport'>
>>> ZZ(RealNumber('4.0'))
4
By default, a warning is issued if a lazy import module is resolved
during Sage’s startup. In case a lazy import’s sole purpose is to
break a circular reference and it is known to be resolved at startup
time, one can use the at_startup
option:
sage: lazy_import('sage.rings.integer_ring', 'ZZ', at_startup=True)
>>> from sage.all import *
>>> lazy_import('sage.rings.integer_ring', 'ZZ', at_startup=True)
This option can also be used as an intermediate step toward not importing by default a module that is used in several places, some of which can already afford to lazy import the module but not all.
A lazy import that is marked as “at_startup” will print a message if it is actually resolved after the startup, so that the developer knows that (s)he can remove the flag:
sage: ZZ
doctest:warning...
UserWarning: Option ``at_startup=True`` for lazy import ZZ not needed anymore
Integer Ring
>>> from sage.all import *
>>> ZZ
doctest:warning...
UserWarning: Option ``at_startup=True`` for lazy import ZZ not needed anymore
Integer Ring
See also
AUTHOR:
Robert Bradshaw
- class sage.misc.lazy_import.LazyImport[source]¶
Bases:
object
EXAMPLES:
sage: from sage.misc.lazy_import import LazyImport sage: my_integer = LazyImport('sage.rings.integer', 'Integer') sage: my_integer(4) 4 sage: my_integer('101', base=2) 5 sage: my_integer(3/2) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer
>>> from sage.all import * >>> from sage.misc.lazy_import import LazyImport >>> my_integer = LazyImport('sage.rings.integer', 'Integer') >>> my_integer(Integer(4)) 4 >>> my_integer('101', base=Integer(2)) 5 >>> my_integer(Integer(3)/Integer(2)) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer
- sage.misc.lazy_import.attributes(a)[source]¶
Return the private attributes of a
LazyImport
object in a dictionary.This is for debugging and doctesting purposes only.
EXAMPLES:
sage: from sage.misc.lazy_import import attributes sage: lazy_import("sage.structure.unique_representation", "foo") sage: attributes(foo)['_namespace'] is globals() True sage: D = attributes(foo) sage: del D['_namespace'] sage: D {'_as_name': 'foo', '_at_startup': False, '_deprecation': None, '_module': 'sage.structure.unique_representation', '_name': 'foo', '_object': None}
>>> from sage.all import * >>> from sage.misc.lazy_import import attributes >>> lazy_import("sage.structure.unique_representation", "foo") >>> attributes(foo)['_namespace'] is globals() True >>> D = attributes(foo) >>> del D['_namespace'] >>> D {'_as_name': 'foo', '_at_startup': False, '_deprecation': None, '_module': 'sage.structure.unique_representation', '_name': 'foo', '_object': None}
- sage.misc.lazy_import.clean_namespace(namespace=None)[source]¶
Adjust
LazyImport
bindings in given namespace to refer to this actual namespace.When
LazyImport
objects are imported into other namespaces via normalimport
instructions, the data stored on aLazyImport
object that helps it to adjust the binding in the namespace to the actual imported object upon access is not adjusted. This routine fixes that.INPUT:
namespace
– the namespace where importing the names; by default, import the names to current namespace
EXAMPLES:
sage: # needs sage.symbolic sage: from sage.misc.lazy_import import attributes, clean_namespace sage: from sage.calculus.calculus import maxima as C sage: attributes(C)['_as_name'] 'maxima' sage: attributes(C)['_namespace'] is sage.calculus.calculus.__dict__ True sage: clean_namespace(globals()) sage: attributes(C)['_as_name'] 'C' sage: attributes(C)['_namespace'] is globals() True
>>> from sage.all import * >>> # needs sage.symbolic >>> from sage.misc.lazy_import import attributes, clean_namespace >>> from sage.calculus.calculus import maxima as C >>> attributes(C)['_as_name'] 'maxima' >>> attributes(C)['_namespace'] is sage.calculus.calculus.__dict__ True >>> clean_namespace(globals()) >>> attributes(C)['_as_name'] 'C' >>> attributes(C)['_namespace'] is globals() True
- sage.misc.lazy_import.ensure_startup_finished()[source]¶
Make sure that the startup phase is finished.
In contrast to
finish_startup()
, this function can be called repeatedly.
- sage.misc.lazy_import.finish_startup()[source]¶
Finish the startup phase.
This function must be called exactly once at the end of the Sage import process (
all
).
- sage.misc.lazy_import.get_star_imports(module_name)[source]¶
Lookup the list of names in a module that would be imported with “import *” either via a cache or actually importing.
EXAMPLES:
sage: from sage.misc.lazy_import import get_star_imports sage: 'get_star_imports' in get_star_imports('sage.misc.lazy_import') True sage: 'EllipticCurve' in get_star_imports('sage.schemes.all') # needs sage.schemes True
>>> from sage.all import * >>> from sage.misc.lazy_import import get_star_imports >>> 'get_star_imports' in get_star_imports('sage.misc.lazy_import') True >>> 'EllipticCurve' in get_star_imports('sage.schemes.all') # needs sage.schemes True
- sage.misc.lazy_import.is_during_startup()[source]¶
Return whether Sage is currently starting up.
OUTPUT: boolean
- sage.misc.lazy_import.lazy_import(module, names, as_=None, at_startup=False, namespace=None, deprecation=None, feature=None)[source]¶
Create a lazy import object and inject it into the caller’s global namespace. For the purposes of introspection and calling, this is like performing a lazy “from module import name” where the import is delayed until the object actually is used or inspected.
INPUT:
module
– string representing the module to importnames
– string or list of strings representing the names to import from moduleas_
– (optional) a string or list of strings representing the names of the objects in the importing module. This is analogous tofrom ... import ... as ...
.at_startup
– boolean (default:False
); whether the lazy import is supposed to be resolved at startup timenamespace
– the namespace where importing the names; by default, import the names to current namespacedeprecation
– (optional) if notNone
, a deprecation warning will be issued when the object is actually imported;deprecation
should be either a trac number (integer) or a pair(issue_number, message)
feature
– a python module (optional), if it cannot be imported an appropriate error is raised
See also
EXAMPLES:
sage: lazy_import('sage.rings.integer_ring', 'ZZ') sage: type(ZZ) <class 'sage.misc.lazy_import.LazyImport'> sage: ZZ(4.0) 4 sage: lazy_import('sage.rings.real_double', 'RDF', 'my_RDF') sage: my_RDF._get_object() is RDF True sage: my_RDF(1/2) 0.5 sage: lazy_import('sage.rings.rational_field', ['QQ', 'frac'], ['my_QQ', 'my_frac']) sage: my_QQ._get_object() is QQ True sage: my_frac._get_object() is sage.rings.rational_field.frac True
>>> from sage.all import * >>> lazy_import('sage.rings.integer_ring', 'ZZ') >>> type(ZZ) <class 'sage.misc.lazy_import.LazyImport'> >>> ZZ(RealNumber('4.0')) 4 >>> lazy_import('sage.rings.real_double', 'RDF', 'my_RDF') >>> my_RDF._get_object() is RDF True >>> my_RDF(Integer(1)/Integer(2)) 0.5 >>> lazy_import('sage.rings.rational_field', ['QQ', 'frac'], ['my_QQ', 'my_frac']) >>> my_QQ._get_object() is QQ True >>> my_frac._get_object() is sage.rings.rational_field.frac True
Upon the first use, the object is injected directly into the calling namespace:
sage: lazy_import('sage.rings.integer_ring', 'ZZ', 'my_ZZ') sage: my_ZZ is ZZ False sage: my_ZZ(37) 37 sage: my_ZZ is ZZ True
>>> from sage.all import * >>> lazy_import('sage.rings.integer_ring', 'ZZ', 'my_ZZ') >>> my_ZZ is ZZ False >>> my_ZZ(Integer(37)) 37 >>> my_ZZ is ZZ True
We check that
lazy_import()
also works for methods:sage: class Foo(): ....: lazy_import('sage.plot.plot', 'plot') sage: class Bar(Foo): ....: pass sage: type(Foo.__dict__['plot']) <class 'sage.misc.lazy_import.LazyImport'> sage: 'EXAMPLES' in Bar.plot.__doc__ # needs sage.plot True sage: type(Foo.__dict__['plot']) # needs sage.plot <... 'function'>
>>> from sage.all import * >>> class Foo(): ... lazy_import('sage.plot.plot', 'plot') >>> class Bar(Foo): ... pass >>> type(Foo.__dict__['plot']) <class 'sage.misc.lazy_import.LazyImport'> >>> 'EXAMPLES' in Bar.plot.__doc__ # needs sage.plot True >>> type(Foo.__dict__['plot']) # needs sage.plot <... 'function'>
If deprecated then a deprecation warning is issued:
sage: lazy_import('sage.rings.padics.factory', 'Qp', 'my_Qp', ....: deprecation=14275) sage: my_Qp(5) # needs sage.rings.padics doctest:...: DeprecationWarning: Importing my_Qp from here is deprecated; please use "from sage.rings.padics.factory import Qp as my_Qp" instead. See https://github.com/sagemath/sage/issues/14275 for details. 5-adic Field with capped relative precision 20
>>> from sage.all import * >>> lazy_import('sage.rings.padics.factory', 'Qp', 'my_Qp', ... deprecation=Integer(14275)) >>> my_Qp(Integer(5)) # needs sage.rings.padics doctest:...: DeprecationWarning: Importing my_Qp from here is deprecated; please use "from sage.rings.padics.factory import Qp as my_Qp" instead. See https://github.com/sagemath/sage/issues/14275 for details. 5-adic Field with capped relative precision 20
An example of deprecation with a message:
sage: lazy_import('sage.rings.padics.factory', 'Qp', 'my_Qp_msg', ....: deprecation=(14275, "This is an example.")) sage: my_Qp_msg(5) # needs sage.rings.padics doctest:...: DeprecationWarning: This is an example. See https://github.com/sagemath/sage/issues/14275 for details. 5-adic Field with capped relative precision 20
>>> from sage.all import * >>> lazy_import('sage.rings.padics.factory', 'Qp', 'my_Qp_msg', ... deprecation=(Integer(14275), "This is an example.")) >>> my_Qp_msg(Integer(5)) # needs sage.rings.padics doctest:...: DeprecationWarning: This is an example. See https://github.com/sagemath/sage/issues/14275 for details. 5-adic Field with capped relative precision 20
An example of an import relying on a feature:
sage: from sage.features import PythonModule sage: lazy_import('ppl', 'equation', ....: feature=PythonModule('ppl', spkg='pplpy', type='standard')) sage: equation # needs pplpy <cyfunction equation at ...> sage: lazy_import('PyNormaliz', 'NmzListConeProperties', ....: feature=PythonModule('PyNormaliz', spkg='pynormaliz')) sage: NmzListConeProperties # optional - pynormaliz <built-in function NmzListConeProperties> sage: lazy_import('foo', 'not_there', ....: feature=PythonModule('foo', spkg='non-existing-package')) sage: not_there Failed lazy import: foo is not available. Importing not_there failed: No module named 'foo'... No equivalent system packages for ... are known to Sage...
>>> from sage.all import * >>> from sage.features import PythonModule >>> lazy_import('ppl', 'equation', ... feature=PythonModule('ppl', spkg='pplpy', type='standard')) >>> equation # needs pplpy <cyfunction equation at ...> >>> lazy_import('PyNormaliz', 'NmzListConeProperties', ... feature=PythonModule('PyNormaliz', spkg='pynormaliz')) >>> NmzListConeProperties # optional - pynormaliz <built-in function NmzListConeProperties> >>> lazy_import('foo', 'not_there', ... feature=PythonModule('foo', spkg='non-existing-package')) >>> not_there Failed lazy import: foo is not available. Importing not_there failed: No module named 'foo'... No equivalent system packages for ... are known to Sage...
- sage.misc.lazy_import.test_fake_startup()[source]¶
For testing purposes only.
Switch the startup lazy import guard back on.
EXAMPLES:
sage: sage.misc.lazy_import.test_fake_startup() sage: lazy_import('sage.rings.integer_ring', 'ZZ', 'my_ZZ') sage: my_ZZ(123) doctest:warning... UserWarning: Resolving lazy import ZZ during startup 123 sage: sage.misc.lazy_import.finish_startup()
>>> from sage.all import * >>> sage.misc.lazy_import.test_fake_startup() >>> lazy_import('sage.rings.integer_ring', 'ZZ', 'my_ZZ') >>> my_ZZ(Integer(123)) doctest:warning... UserWarning: Resolving lazy import ZZ during startup 123 >>> sage.misc.lazy_import.finish_startup()