#19628: lazy_import breaks UniqueRepresentation
-------------------------------------+-------------------------------------
       Reporter:  cheuberg           |        Owner:
           Type:  defect             |       Status:  new
       Priority:  major              |    Milestone:  sage-6.10
      Component:  coercion           |   Resolution:
       Keywords:                     |    Merged in:
        Authors:                     |    Reviewers:
Report Upstream:  N/A                |  Work issues:
         Branch:  u/cheuberg/19152   |       Commit:
  -arb-misc-lazy-import              |  b21aa6e069182b08aca3cf7c24d6ed24e5926510
   Dependencies:                     |     Stopgaps:
-------------------------------------+-------------------------------------

Comment (by nbruin):

 There are many subtle problems with lazy_import and some of them are
 fairly fundamental. The basic take-away is: don't lazy_import objects, but
 lazy_import functions that produce objects (`NN` is problematic but
 `NonNegativeIntegerSemiring()` is not).

 `LazyImport` objects try to be as transparent as possible, but obviously
 won't succeed perfectly. They try to remove themselves once non-trivially
 accessed (that is, basically when one of their attributes gets accessed),
 and essentially LazyImport objects should ''only'' be used to look up
 attributes on.

 Many of our `LazyImport` objects get further imported erroneously. For
 instance, importing a `LazyImport` object from elsewhere via a `from ...
 import <LazyImportObject>` doesn't work properly, because the LazyImport
 object doesn't get an opportunity to know about the new namespace in which
 it gets imported. An example is `NN` as referenced here.

 If we insert a little helper function into `sage.misc.lazy_import` (it has
 to be there because `lazy_import` doesn't offer a `pxd`):
 {{{
 def att(a):
     cdef LazyImport b
     b = a
     return {"_object": b._object,
             "_module": b._module,
             "_name": b._name,
             "_as_name": b._as_name,
             "_namespace": b._namespace,
             "_at_startup": b._at_startup,
             "_deprecation": b._deprecation}
 }}}
 then you can see from
 {{{
 sage: sage.misc.lazy_import.att(NN)['_namespace']['__name__']
 'sage.rings.semirings.all'
 sage: sage.misc.lazy_import.att(NN)['_name']
 'NN'
 sage: sage.misc.lazy_import.att(NN)['_as_name']
 'NN'
 }}}
 that accessing `NN` in the toplevel scope will never resolve. On every
 access, the `LazyImport` object will dutifully search the
 `sage.rings.semirings.all` scope to replace its occurrence there (which
 only happens the first time of course), but that's not the binding through
 which it gets accessed afterwards.

 If instead you do (and this is how NN should be imported into the toplevel
 if at all):
 {{{
 sage: lazy_import(sage.misc.lazy_import.att(NN)['_module'],
 sage.misc.lazy_import.att(NN)['_name'])
 sage: type(NN)
 <type 'sage.misc.lazy_import.LazyImport'>
 sage: NN
 Non negative integer semiring
 sage: type(NN)
 <class
 
'sage.rings.semirings.non_negative_integer_semiring.NonNegativeIntegerSemiring_with_category'>
 }}}
 As you can see, basically even
 {{{
 from sage.rings.semirings.non_negative_integer_semiring import NN
 }}}
 is an erroneous use of a `LazyImport` object, because it does not amount
 to attribute lookup.

 The reason why mathematically interesting objects themselves should
 probably not be lazily imported, but only their access functions is
 because
 {{{
 sage: { NN : 1 }
 }}}
 will always be problematic: The LazyImport object doesn't get a chance to
 remove itself.

 On the other hand, accessing the object via `NonNegativeIntegerSemiring()`
 works nicely.

 Of course, `NonNegativeIntegerSemiring` itself is incorrectly imported.

 We could clean up scopes (but that should happen in basically all
 `__all__` files etc.) via something along the lines of:

 {{{
 scope=globals()
 for name,value in scope.iteritems():
     if isinstance(value,sage.misc.lazy_import.LazyImport):
         T= sage.misc.lazy_import.att(value)
         if T['_namespace'] is not scope:
             scope[name] =
 
sage.misc.lazy_import.LazyImport(T['_module'],T['_name'],T['_as_name'],scope,T['_at_startup'],T['_deprecation'])
 }}}

 Rather than hobbling EqualityById to accommodate for the hack that is
 `LazyImport`, we should avoid erroneous use of `LazyImport`. The problem
 with `EqualityById` is just a symptom of a general problem. There are many
 tickets about this, see e.g. #12482

--
Ticket URL: <http://trac.sagemath.org/ticket/19628#comment:12>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/d/optout.

Reply via email to