Skip to content

lazy_import doesn't resolve properly when indirectly imported #16522

@nbruin

Description

@nbruin

We have several places where lazy_import objects are used in a way that prevents them from behaving as designed.

The original idea of LazyImport proxies is that they have a pointer to the namespace in which they are bound, so that once the import gets triggered, the proxy object redirects the binding in the namespace to point straight to the proxied object. Once this redirection has happened, the proxy object should not play a role anymore and no performance impact should happen at all.

The problem occurs from statements such as:

calculus/all.py:1:from calculus import maxima as maxima_calculus

This doesn't work, because this is a LazyImport proxy, which needs to know the namespace in which it is bound to do the proper replacement. This one is tied to sage.calculus.calculus.maxima, so it can't rebind the global maxima_calculus. Indeed:

sage: type(sage.calculus.calculus.maxima)
<type 'sage.misc.lazy_import.LazyImport'>
sage: type(maxima_calculus)
<type 'sage.misc.lazy_import.LazyImport'>
sage: hash(maxima_calculus)
-7971541566211231133
sage: type(sage.calculus.calculus.maxima)
<class 'sage.interfaces.maxima_lib.MaximaLib'>
sage: type(maxima_calculus)
<type 'sage.misc.lazy_import.LazyImport'>

The binding of maxima_calculus in the global namespace (and the one in calculus.all too) remains to the LazyImport proxy. Thus we suffer from indirection overhead [one might worry we'd suffer repeated extraneous dictionary modifications, but LazyImport is smart enough to only attempt to rebind sage.calculus.calculus.maxima only on the first access] as well as problems that things like id(LazyImportShim) and type(LazyImportShim) are not what they're supposed to model.

If instead we do:

sage: lazy_import('sage.interfaces.maxima_lib','maxima','maxima_calculus')

we see that things do resolve:

sage: type(maxima_calculus)
<type 'sage.misc.lazy_import.LazyImport'>
sage: hash(maxima_calculus)
-7971541566211231133
sage: type(maxima_calculus)
<class 'sage.interfaces.maxima_lib.MaximaLib'>

Other bindings need their own chance to resolve, but do:

sage: type(sage.calculus.calculus.maxima)
<type 'sage.misc.lazy_import.LazyImport'>
sage: hash(sage.calculus.calculus.maxima)
-7971541566211231133
sage: type(sage.calculus.calculus.maxima)
<class 'sage.interfaces.maxima_lib.MaximaLib'>

The obvious fix: in calculus.all, import maximalib directly and lazily, rather than indirectly from sage.calculus.calculus only kicks the can further, since in sage.all we have from sage.calculus.all import * (which I think is where it really gets placed in the global sage namespace).

CC: @kcrisman @embray @tscrim

Component: misc

Author: Nils Bruin, Matthias Koeppe

Branch/Commit: 9837dec

Reviewer: Matthias Koeppe, Nils Bruin

Issue created by migration from https://trac.sagemath.org/ticket/16522

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions