Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use importlib to load numba extensions #5209

Merged
merged 26 commits into from
Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2b672f2
Use importlib to load numba extensions
Feb 6, 2020
bedf8da
Optimize imports
Feb 6, 2020
4999274
Add missing deps on importlib-metadata backport
Feb 6, 2020
22f4f04
Fix selector and backport name in conda recipe
Feb 7, 2020
22e91be
Add more meaningful message for missing importlib_metadata
Feb 10, 2020
8f3c285
Update run time dependencies in installing.rst
Feb 10, 2020
760ae11
Add importlib_metadata dep to install_requires in setup.py
Feb 11, 2020
c3d6990
Install importlib_metadata using conda in build scripts
Feb 12, 2020
44bcc9c
Merge branch 'master' into fix/4927-slow-import
Mar 3, 2020
22e230a
Use empty tuple literal as default value
Apr 2, 2020
86af993
Update setup.py
svrakitin Aug 19, 2021
0d03446
Merge remote-tracking branch 'numba/master' into fix/4927-slow-import
gmarkall Aug 19, 2021
bbe8594
Update warning message to use module name from importlib metadata
gmarkall Aug 20, 2021
1fbb79f
Update tests added since #5209 made
gmarkall Aug 20, 2021
6184d29
Fix entry point module name determination on Python 3.8
gmarkall Aug 20, 2021
7992f28
Revert "Fix entry point module name determination on Python 3.8"
gmarkall Aug 23, 2021
169d106
Use importlib_metadata on Python 3.8 (PR #5209 feedback)
gmarkall Aug 23, 2021
e5fdc82
test_entrypoints: Use importlib_metadata based on Python version
gmarkall Aug 23, 2021
61ae870
Remove obsolete comment (PR #5209 feedback)
gmarkall Aug 31, 2021
c842eda
setup.py: Requite importlib_metadata for Python < 3.9
gmarkall Aug 31, 2021
724990f
Use `select()` method of `entry_points` as appropriate
stuartarchibald Jan 25, 2022
d94e66d
Merge remote-tracking branch 'numba/master' into fix/4927-slow-import
gmarkall Jan 25, 2022
fbd66c3
Merge remote-tracking branch 'numba/master' into fix/4927-slow-import
gmarkall Jan 26, 2022
b5f75f6
Fix comments in build scripts (PR #5209 feedback)
gmarkall Jan 26, 2022
59fa60d
Use a mock for test_entrypoint_tolerance (PR #5209 feedback)
gmarkall Jan 26, 2022
4456c37
Move comment in build script
gmarkall Jan 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions buildscripts/condarecipe.local/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ requirements:
- llvmlite 0.31.*
- funcsigs # [py27]
- singledispatch # [py27]
- importlib-metadata # [py27]
esc marked this conversation as resolved.
Show resolved Hide resolved
# TBB devel version is to match TBB libs
- tbb-devel >=2018.0.5 # [not ((armv6l or armv7l or aarch64) or (win and py27))]
run:
Expand All @@ -45,6 +46,7 @@ requirements:
- llvmlite 0.31.*
- funcsigs # [py27]
- singledispatch # [py27]
- importlib-metadata # [py27]
esc marked this conversation as resolved.
Show resolved Hide resolved
run_constrained:
# If TBB is present it must be at least this version from Anaconda due to
# build flag issues triggering UB
Expand Down
1 change: 1 addition & 0 deletions buildscripts/incremental/setup_conda_environment.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ call activate %CONDA_ENV%
if %PYTHON% LSS 3.4 (%CONDA_INSTALL% enum34)
if %PYTHON% LSS 3.4 (%PIP_INSTALL% singledispatch)
if %PYTHON% LSS 3.3 (%CONDA_INSTALL% -c numba funcsigs)
if %PYTHON% LSS 3.8 (%PIP_INSTALL% importlib-metadata)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps get this from the Anaconda distro?!

@rem Install dependencies for building the documentation
if "%BUILD_DOC%" == "yes" (%CONDA_INSTALL% sphinx pygments)
if "%BUILD_DOC%" == "yes" (%PIP_INSTALL% sphinx_bootstrap_theme)
Expand Down
2 changes: 2 additions & 0 deletions buildscripts/incremental/setup_conda_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ $CONDA_INSTALL -c numba llvmlite
# Install enum34 and singledispatch for Python < 3.4
if [ $PYTHON \< "3.4" ]; then $CONDA_INSTALL enum34; fi
if [ $PYTHON \< "3.4" ]; then $PIP_INSTALL singledispatch; fi
# Install importlib-metadata for Python < 3.8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Install importlib-metadata for Python < 3.8
# Install importlib-metadata for Python < 3.9

if [ $PYTHON \< "3.8" ]; then $PIP_INSTALL importlib-metadata; fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps get this from the Anaconda distro?!

# Install funcsigs for Python < 3.3
if [ $PYTHON \< "3.3" ]; then $CONDA_INSTALL -c numba funcsigs; fi
# Install dependencies for building the documentation
Expand Down
16 changes: 11 additions & 5 deletions numba/core/entrypoints.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import logging

from pkg_resources import iter_entry_points
try:
from importlib import metadata as importlib_metadata
except ImportError:
import importlib_metadata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if this fails? I'm wondering if there ought to be an explicit Python>=3.8 branch which just imports it and then another which tries and fails gracefully if the package is missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will only fail on Python < 3.8 if importlib_metadata is missing (it is listed as requirement).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to assume nothing about how Numba was built, it's entirely possible someone just grabbed the source and some limited set of bootstrap packages available for their unusual architecture and built it (this actually happens in practice, we see these people on the issue tracker when strange things happen!). As a result, we tend to err on the side of trying to be as helpful as possible and catch things like this and say something about it being needed.

Further, this will need adding to the dependencies section in the docs: https://github.com/numba/numba/blob/13ece9b97e6f01f750e870347f231282325f60c3/docs/source/user/installing.rst#dependency-list


_already_initialized = False
logger = logging.getLogger(__name__)
Expand All @@ -18,7 +21,10 @@ def init_all():
# Must put this here to avoid extensions re-triggering initialization
_already_initialized = True

for entry_point in iter_entry_points('numba_extensions', 'init'):
logger.debug('Loading extension: %s', entry_point)
func = entry_point.load()
func()
for entry_point in importlib_metadata.entry_points().get(
'numba_extensions', tuple()
):
if entry_point.name == 'init':
logger.debug('Loading extension: %s', entry_point)
func = entry_point.load()
func()
54 changes: 25 additions & 29 deletions numba/tests/test_entrypoints.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import sys
import types
from unittest import mock

import pkg_resources
try:
from importlib import metadata as importlib_metadata
except ImportError:
import importlib_metadata

from numba.tests.support import TestCase

Expand All @@ -15,45 +18,38 @@ def test_init_entrypoint(self):
# loosely based on Pandas test from:
# https://github.com/pandas-dev/pandas/pull/27488

# FIXME: Python 2 workaround because nonlocal doesn't exist
counters = {'init': 0}

def init_function():
counters['init'] += 1

mod = types.ModuleType("_test_numba_extension")
mod.init_func = init_function
mod = mock.Mock(__name__='_test_numba_extension')

try:
# will remove this module at the end of the test
sys.modules[mod.__name__] = mod

# We are registering an entry point using the "numba" package
# ("distribution" in pkg_resources-speak) itself, though these are
# ("distribution" in importlib-speak) itself, though these are
# normally registered by other packages.
stuartarchibald marked this conversation as resolved.
Show resolved Hide resolved
dist = "numba"
entrypoints = pkg_resources.get_entry_map(dist)
my_entrypoint = pkg_resources.EntryPoint(
"init", # name of entry point
mod.__name__, # module with entry point object
attrs=['init_func'], # name of entry point object
dist=pkg_resources.get_distribution(dist)
my_entrypoint = importlib_metadata.EntryPoint(
'init', '_test_numba_extension:init_func', 'numba_extensions',
)
entrypoints.setdefault('numba_extensions',
{})['init'] = my_entrypoint

from numba.core import entrypoints
# Allow reinitialization
entrypoints._already_initialized = False
with mock.patch.object(
importlib_metadata,
'entry_points',
return_value={'numba_extensions': (my_entrypoint,)},
):

from numba.core import entrypoints

# Allow reinitialization
entrypoints._already_initialized = False
stuartarchibald marked this conversation as resolved.
Show resolved Hide resolved

entrypoints.init_all()
entrypoints.init_all()

# was our init function called?
self.assertEqual(counters['init'], 1)
# was our init function called?
mod.init_func.assert_called_once()
Comment on lines -68 to +64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a similar change can also be made to test_entrypoint_tolerance as part of this clean up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar change made - note this also involves configuring the mock with a side effect for init_func().


# ensure we do not initialize twice
entrypoints.init_all()
self.assertEqual(counters['init'], 1)
# ensure we do not initialize twice
entrypoints.init_all()
mod.init_func.assert_called_once()
finally:
# remove fake module
if mod.__name__ in sys.modules:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ llvmlite>=0.31
argparse
funcsigs ; python_version < '3.4'
singledispatch ; python_version < '3.4'
importlib-metadata ; python_version < '3.8'