On Wed, 13 May 2020 at 23:36, Jason Moore <[email protected]> wrote:
>
> I had a look at the backwards incompatibilities.
>
> This one stood out:
>
> Submodule names are no longer imported with from sympy import *. They can 
> still be imported directly like from sympy import core or accessed like 
> sympy.core, or like sys.modules['sympy.simplify'] for modules that share 
> names with SymPy functions.
>
> Is this really necessary? On the face of it, I suspect this could cause a 
> fair amount of code in the wild to break. I'm not sure what breaking this 
> functionality gains, but it surely may cause down stream pain.

Some code might break but in future we will have better control of
what is or is not imported and be in a better position to prevent
accidental breakage. The release note emphasises what you might need
to worry about rather than the rationale for the change so I'll
explain that here.

The main change is not that submodules are no longer imported but that
the codebase explicitly lists what is imported from each package using
__all__. This means that imports can never be accidental as they must
be explicitly listed at each chain of package to package import.
Ultimately something is not importable from the top-level sympy
namespace unless it is explicitly listed in __all__ in
sympy/__init__.py.

An example of the kind of report (on github or stackoverflow) that I
have seen is this from sympy 1.5.1:

    >>> from sympy import pi, evalf
    >>> evalf(pi)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'module' object is not callable

(Actually I've just realised this still happens on 1.6 because evalf
is listed in __all__...)

What has happened here is that the sympy.core.evalf module has been
imported unintentionally and appears as the reasonably named function
evalf (why is there no evalf function?). I don't think it was ever
intended that the evalf module would be importable in this way. It's
just a consequence of the fact that in 1.5 sympy/__init__.py uses

    from .core import *

and then does not define __all__ itself.

Another example that I have seen reports of is this:

    >>> from sympy import core
    >>> core
    <module 'sympy.core.core' from
'/Users/enojb/current/sympy/sympy/sympy/core/core.py'>

Note that this has imported sympy.core.core rather than sympy.core.

In other cases what is imported depends on which previous imports have occurred:

    >>> from sympy import *
    >>> stats
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'stats' is not defined
    >>> import sympy.stats
    >>> from sympy import *
    >>> stats
    <module 'sympy.stats' from
'/Users/enojb/current/sympy/sympy/sympy/stats/__init__.py'>

Again this is unintentional. Using * import without __all__ has always
been flakey.

So the real change here is not that submodules are removed from
star-import but that star-import is made well defined and
controllable. Since it is now well-defined and controllable we don't
need to allow imports of submodules purely by accident. Of course at
that point we can decide whether we want to list all of the
subpackages in __all__. Since these were only ever there by accident I
decided to remove them.

--
Oscar

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sympy/CAHVvXxSMRj8WZN_msDsL4qBeCpyd4Ts7O1wa_oJvgThZ7THVKw%40mail.gmail.com.

Reply via email to