Re: move __init__.py's import of importlib into __getattr__?

Ralf Gommers <[email protected]> Wed, 9 Aug 2023 09:50:37 +0200
Newsgroups gmane.comp.python.scientific.devel
Message-ID <CABL7CQj+4gmSxTj9s1PCxSqY2cA4yOK66qiYYE3vz7--SEbnbw@mail.gmail.com>
On Wed, Aug 9, 2023 at 12:50 AM Nathan Jensen <[email protected]> wrote:

> Hi,
>
> On a project I work on we encountered a complicated issue when upgrading
> from scipy 1.8 to scipy 1.10, and we traced it to being introduced in scipy
> 1.9 with the changes to how scipy submodules are imported.  The issue is
> described here: https://github.com/ninia/jep/issues/487
>

Hi Nathan, thanks for bringing this up. That's a nice analysis in the
linked issue.


> Some background: The application uses Jep which enables Java processes to
> run CPython inside the process.  Jep supports two types of interpreters,
> SubInterpreters which use CPython's sub-interpreter API, and
> SharedInterpreters which do not.  The application is using SubInterpreters
> because it predates Jep's introduction of SharedInterpreters by many years
> and there are pros and cons of each type of interpreter and it's not
> trivial to switch.  Because most CPython extension modules are not coded or
> tested with sub-interpreters in mind, Jep introduced a concept called
> "shared modules" that shares a module across sub-interpreters and ensures
> it is only ever imported once per process and is never disposed of or
> garbage collected in that process.  scipy is imported as a shared module so
> it will work in sub-interpreters but then the usage of importlib is messing
> up the subpackage import.  It's complicated but explained in that issue I
> linked above.
>
> We found a fix for the issue is to move the import of importlib in scipy's
> __init__.py into the __getattr__ method.
> https://github.com/scipy/scipy/blob/main/scipy/__init__.py#L185
>
> So what I'm wondering is, if I submitted a pull request that moved the
> import of importlib into __getattr__, either before the if name in modules
> statement or inside the if name in modules statement, is that a change that
> the scipy team would consider accepting for a future release?
>

It looks like the overhead of this is ~70 ns on my machine per function
call if made like `scipy.submodule.func()`, because of repeating the
`import importlib` statement every time. That is also the typical overhead
of a Python function call; it's not negligible, but may be acceptable for
SciPy.

On the other hand, what we are using here is the exact pattern advertised
for lazy submodule importing from https://peps.python.org/pep-0562/. So
this is going to be used in many other packages (e.g., it's encouraged at
https://scientific-python.org/specs/spec-0001/ and used in scikit-learn,
NetworkX, and possibly soon in NumPy). This is exactly the kind of obscure
issue that tends to surface when doing things like lazy imports - this is
the first such issue I believe though, so far it's been pretty robust
compared to all the hacky ways of doing lazy imports from 10+ years ago.
It's not clear to me if this is fixable in either CPython or in Jep after
something is changed to enable this use case in `importlib`. So I'd suggest
to also open an issue on the CPython repo and see what can be done on the
importlib side.

Cheers,
Ralf

_______________________________________________
SciPy-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/scipy-dev.python.org/
Member address: [email protected]