Skip to content

Commit

Permalink
Fix numba#8903. NumbaDeprecationWarnings raised from `@{gu,}vectori…
Browse files Browse the repository at this point in the history
…ze`.

This adds localised suppressions for `NumbaDeprecationWarning`s that
would have been raised from the code path for `@{gu,}vectorize` due to
Numba's internal use of `@jit()` with the `nopython` kwarg not present.

This is considered acceptable due to the following: Deprecation warnings
from Numba's internal use of its own API should not be visible to users.
Unfortunately there's no trivial programmatic way to work around the
internal use of `@jit`-with-no-kwargs in this case as numerous code
paths go through the same operations and user code may well be relying on
"object mode" fall-back. What this patch does it makes it so that users will
not see `NumbaDeprecationWarning`s for this internal use. However, if a
user defines a `@{gu,}vectorize` function that ends up using the fall-back
to the object mode pipeline during compilation, Numba will continue to raise
numerous loud warnings about this happening which should provide a hint as
to the problem and how to fix it.

Numerous tests have been added to check combinations of kwargs/use of
default kwargs being passed through to `@{gu,}vectorize`. These tests
make sure that there are no additional `NumbaDeprecationWarning`s
raised to do with Numba's internal use of `@jit()` in `@{gu,}vectorize`
where the `nopython` kwarg is not supplied.

Closes numba#8903
  • Loading branch information
stuartarchibald committed Apr 19, 2023
1 parent 74a0840 commit a1bf88a
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 26 deletions.
7 changes: 4 additions & 3 deletions numba/np/ufunc/dufunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ class DUFunc(serialize.ReduceMixin, _internal._DUFunc):
def __init__(self, py_func, identity=None, cache=False, targetoptions={}):
if is_jitted(py_func):
py_func = py_func.py_func
dispatcher = jit(_target='npyufunc',
cache=cache,
**targetoptions)(py_func)
with ufuncbuilder._suppress_deprecation_warning_nopython_not_supplied():
dispatcher = jit(_target='npyufunc',
cache=cache,
**targetoptions)(py_func)
self._initialize(dispatcher, identity)
functools.update_wrapper(self, py_func)

Expand Down
26 changes: 22 additions & 4 deletions numba/np/ufunc/ufuncbuilder.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# -*- coding: utf-8 -*-

import inspect
import warnings
from contextlib import contextmanager

from numba.core import config, targetconfig
from numba.core.decorators import jit
from numba.core.descriptors import TargetDescriptor
from numba.core.extending import is_jitted
from numba.core.errors import NumbaDeprecationWarning
from numba.core.options import TargetOptions, include_default_options
from numba.core.registry import cpu_target
from numba.core.target_extension import dispatcher_registry, target_registry
Expand Down Expand Up @@ -230,6 +232,20 @@ def parse_identity(identity):
return identity


@contextmanager
def _suppress_deprecation_warning_nopython_not_supplied():
"""This suppresses the NumbaDeprecationWarning that occurs through the use
of `jit` without the `nopython` kwarg. This use of `jit` occurs in a few
places in the `{g,}ufunc` mechanism in Numba, predominantly to wrap the
"kernel" function."""
with warnings.catch_warnings():
warnings.filterwarnings('ignore',
category=NumbaDeprecationWarning,
message=(".*The 'nopython' keyword argument "
"was not supplied*"),)
yield


# Class definitions

class _BaseUFuncBuilder(object):
Expand Down Expand Up @@ -260,9 +276,10 @@ def __init__(self, py_func, identity=None, cache=False, targetoptions={}):
py_func = py_func.py_func
self.py_func = py_func
self.identity = parse_identity(identity)
self.nb_func = jit(_target='npyufunc',
cache=cache,
**targetoptions)(py_func)
with _suppress_deprecation_warning_nopython_not_supplied():
self.nb_func = jit(_target='npyufunc',
cache=cache,
**targetoptions)(py_func)
self._sigs = []
self._cres = {}

Expand Down Expand Up @@ -325,7 +342,8 @@ def __init__(self, py_func, signature, identity=None, cache=False,
targetoptions={}, writable_args=()):
self.py_func = py_func
self.identity = parse_identity(identity)
self.nb_func = jit(_target='npyufunc', cache=cache)(py_func)
with _suppress_deprecation_warning_nopython_not_supplied():
self.nb_func = jit(_target='npyufunc', cache=cache)(py_func)
self.signature = signature
self.sin, self.sout = parse_signature(signature)
self.targetoptions = targetoptions
Expand Down
2 changes: 1 addition & 1 deletion numba/tests/npyufunc/test_ufunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def test_ufunc_exception_on_write_to_readonly(self):
tests = []
expect = "ufunc 'sin' called with an explicit output that is read-only"
tests.append((jit(nopython=True), TypingError, expect))
tests.append((jit(nopython=False), ValueError,
tests.append((jit(forceobj=True), ValueError,
"output array is read-only"))

for dec, exc, msg in tests:
Expand Down
2 changes: 1 addition & 1 deletion numba/tests/npyufunc/test_vectorize_decor.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class TestParallelVectorizeDecor(unittest.TestCase, BaseVectorizeDecor):

class TestCPUVectorizeJitted(unittest.TestCase, BaseVectorizeDecor):
target = 'cpu'
wrapper = jit
wrapper = jit(nopython=True)


class BaseVectorizeNopythonArg(unittest.TestCase, CheckWarningsMixin):
Expand Down
252 changes: 235 additions & 17 deletions numba/tests/test_deprecations.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import warnings
from numba import jit, generated_jit
import unittest
from contextlib import contextmanager

from numba import jit, generated_jit, vectorize, guvectorize
from numba.core.errors import (NumbaDeprecationWarning,
NumbaPendingDeprecationWarning, NumbaWarning)
from numba.tests.support import TestCase
import unittest


@contextmanager
def _catch_numba_deprecation_warnings():
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("ignore", category=NumbaWarning)
warnings.simplefilter("always", category=NumbaDeprecationWarning)
yield w


class TestDeprecation(TestCase):
Expand All @@ -18,9 +28,7 @@ def check_warning(self, warnings, expected_str, category):
def test_jitfallback(self):
# tests that @jit falling back to object mode raises a
# NumbaDeprecationWarning
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("ignore", category=NumbaWarning)
warnings.simplefilter("always", category=NumbaDeprecationWarning)
with _catch_numba_deprecation_warnings() as w:
# ignore the warning about the nopython kwarg not being supplied
warnings.filterwarnings("ignore",
message=(r".*The 'nopython' keyword "
Expand All @@ -40,9 +48,7 @@ def foo():
def test_default_missing_nopython_kwarg(self):
# test that not supplying `nopython` kwarg to @jit raises a warning
# about the default changing.
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("ignore", category=NumbaWarning)
warnings.simplefilter("always", category=NumbaDeprecationWarning)
with _catch_numba_deprecation_warnings() as w:

@jit
def foo():
Expand All @@ -58,9 +64,7 @@ def test_explicit_false_nopython_kwarg(self):
# tests that explicitly setting `nopython=False` in @jit raises a
# warning about the default changing and it being an error in the
# future.
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("ignore", category=NumbaWarning)
warnings.simplefilter("always", category=NumbaDeprecationWarning)
with _catch_numba_deprecation_warnings() as w:

@jit(nopython=False)
def foo():
Expand All @@ -76,9 +80,7 @@ def test_default_missing_nopython_kwarg_silent_if_forceobj(self):
# Checks that if forceobj is set and the nopython kwarg is also not
# present then no warning is raised. The user intentially wants objmode.

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("ignore", category=NumbaWarning)
warnings.simplefilter("always", category=NumbaDeprecationWarning)
with _catch_numba_deprecation_warnings() as w:

@jit(forceobj=True)
def foo():
Expand All @@ -89,6 +91,224 @@ def foo():
# no warnings should be raised.
self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_vectorize_missing_nopython_kwarg_not_reported(self):
# Checks that use of @vectorize without a nopython kwarg doesn't raise
# a warning about lack of said kwarg.

with _catch_numba_deprecation_warnings() as w:
# This compiles via nopython mode directly
@vectorize('float64(float64)')
def foo(a):
return a + 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_vectorize_nopython_false_is_reported(self):
# Checks that use of @vectorize with nopython=False raises a warning
# about supplying it.

with _catch_numba_deprecation_warnings() as w:
# This compiles via nopython mode directly
@vectorize('float64(float64)', nopython=False)
def foo(a):
return a + 1

msg = "The keyword argument 'nopython=False' was supplied"
self.check_warning(w, msg, NumbaDeprecationWarning)

@TestCase.run_test_in_subprocess
def test_vectorize_objmode_missing_nopython_kwarg_not_reported(self):
# Checks that use of @vectorize without a nopython kwarg doesn't raise
# a warning about lack of the nopython kwarg, but does raise a warning
# about falling back to obj mode to compile.

with _catch_numba_deprecation_warnings() as w:
# This compiles via objmode fallback
@vectorize('float64(float64)')
def foo(a):
object()
return a + 1

msg = ("Fall-back from the nopython compilation path to the object "
"mode compilation path")
self.check_warning(w, msg, NumbaDeprecationWarning)

@TestCase.run_test_in_subprocess
def test_vectorize_objmode_nopython_false_is_reported(self):
# Checks that use of @vectorize with a nopython kwarg set to False
# doesn't raise a warning about lack of the nopython kwarg, but does
# raise a warning about it being False and then a warning about falling
# back to obj mode to compile.

with _catch_numba_deprecation_warnings() as w:
# This compiles via objmode fallback with a warning about
# nopython=False supplied.
@vectorize('float64(float64)', nopython=False)
def foo(a):
object()
return a + 1

# 2 warnings: 1. use of nopython=False, then, 2. objmode fallback
self.assertEqual(len(w), 2)

msg = "The keyword argument 'nopython=False' was supplied"
self.check_warning([w[0]], msg, NumbaDeprecationWarning)

msg = ("Fall-back from the nopython compilation path to the object "
"mode compilation path")
self.check_warning([w[1]], msg, NumbaDeprecationWarning)

@TestCase.run_test_in_subprocess
def test_vectorize_objmode_direct_compilation_no_warnings(self):
# Checks that use of @vectorize with forceobj=True raises no warnings.

with _catch_numba_deprecation_warnings() as w:
# Compiles via objmode directly with no warnings raised
@vectorize('float64(float64)', forceobj=True)
def foo(a):
object()
return a + 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_vectorize_objmode_compilation_nopython_false_no_warnings(self):
# Checks that use of @vectorize with forceobj set and nopython set as
# False raises no warnings.

with _catch_numba_deprecation_warnings() as w:
# Compiles via objmode directly with no warnings raised
@vectorize('float64(float64)', forceobj=True, nopython=False)
def foo(a):
object()
return a + 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_vectorize_parallel_true_no_warnings(self):
# Checks that use of @vectorize with the parallel target doesn't
# raise warnings about nopython kwarg, the parallel target doesn't
# support objmode so nopython=True is implicit.
with _catch_numba_deprecation_warnings() as w:
@vectorize('float64(float64)', target='parallel')
def foo(x):
return x + 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_vectorize_parallel_true_nopython_true_no_warnings(self):
# Checks that use of @vectorize with the parallel target and
# nopython=True doesn't raise warnings about nopython kwarg.
with _catch_numba_deprecation_warnings() as w:
@vectorize('float64(float64)', target='parallel', nopython=True)
def foo(x):
return x + 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_vectorize_parallel_true_nopython_false_warns(self):
# Checks that use of @vectorize with the parallel target and
# nopython=False raises a warning about the nopython kwarg being False.
with _catch_numba_deprecation_warnings() as w:
@vectorize('float64(float64)', target='parallel', nopython=False)
def foo(x):
return x + 1

msg = "The keyword argument 'nopython=False' was supplied"
self.check_warning(w, msg, NumbaDeprecationWarning)

@TestCase.run_test_in_subprocess
def test_vectorize_calling_jit_with_nopython_false_warns_from_jit(self):
# Checks the scope of the suppression of deprecation warnings that are
# present in e.g. vectorize. The function `bar` should raise a
# deprecation warning, the `@vectorize`d `foo` function should not,
# even though both don't have a nopython kwarg.

# First check that the @vectorize call doesn't raise anything
with _catch_numba_deprecation_warnings() as w:
@vectorize('float64(float64)', forceobj=True)
def foo(x):
return bar(x + 1)

def bar(*args):
pass

self.assertFalse(w)

# Now check the same compilation but this time compiling through to a
# @jit call.
with _catch_numba_deprecation_warnings() as w:
@vectorize('float64(float64)', forceobj=True)
def foo(x):
return bar(x + 1)

@jit
def bar(x):
return x

msg = "The 'nopython' keyword argument was not supplied"
self.check_warning(w, msg, NumbaDeprecationWarning)

@TestCase.run_test_in_subprocess
def test_guvectorize_implicit_nopython_no_warnings(self):
# Checks that use of @guvectorize with implicit nopython compilation
# does not warn on compilation.
with _catch_numba_deprecation_warnings() as w:

@guvectorize('void(float64[::1], float64[::1])', '(n)->(n)')
def bar(a, b):
a += 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_guvectorize_forceobj_no_warnings(self):
# Checks that use of @guvectorize with direct objmode compilation does
# not warn.
with _catch_numba_deprecation_warnings() as w:

@guvectorize('void(float64[::1], float64[::1])', '(n)->(n)',
forceobj=True)
def bar(a, b):
object()
a += 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_guvectorize_parallel_implicit_nopython_no_warnings(self):
# Checks that use of @guvectorize with parallel target and implicit
# nopython mode compilation does not warn.
with _catch_numba_deprecation_warnings() as w:

@guvectorize('void(float64[::1], float64[::1])', '(n)->(n)',
target='parallel')
def bar(a, b):
a += 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_guvectorize_parallel_forceobj_no_warnings(self):
# Checks that use of @guvectorize with parallel target and direct
# objmode compilation does not warn.
with _catch_numba_deprecation_warnings() as w:

# This compiles somewhat surprisingly for the parallel target using
# object mode?!
@guvectorize('void(float64[::1], float64[::1])', '(n)->(n)',
target='parallel', forceobj=True)
def bar(a, b):
object()
a += 1

self.assertFalse(w)

@TestCase.run_test_in_subprocess
def test_reflection_of_mutable_container(self):
# tests that reflection in list/set warns
Expand Down Expand Up @@ -119,9 +339,7 @@ def foo_set(a):
@TestCase.run_test_in_subprocess
def test_generated_jit(self):

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("ignore", category=NumbaWarning)
warnings.simplefilter("always", category=NumbaDeprecationWarning)
with _catch_numba_deprecation_warnings() as w:

@generated_jit
def bar():
Expand Down

0 comments on commit a1bf88a

Please sign in to comment.