Files
2025-05-20 11:57:43 -04:00

223 lines
7.7 KiB
Python

import os
import subprocess
import sys
import warnings
import numpy as np
import unittest
from numba import jit
from numba.core.errors import NumbaWarning, deprecated, NumbaDeprecationWarning
from numba.core import errors
from numba.tests.support import ignore_internal_warnings
class TestBuiltins(unittest.TestCase):
def check_objmode_deprecation_warning(self, w):
# Object mode fall-back is slated for deprecation, check the warning
msg = ("Fall-back from the nopython compilation path to the object "
"mode compilation path has been detected")
self.assertEqual(w.category, NumbaDeprecationWarning)
self.assertIn(msg, str(w.message))
def test_type_infer_warning(self):
def add(x, y):
a = {} # noqa dead
return x + y
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always', NumbaWarning)
ignore_internal_warnings()
cfunc = jit(add)
cfunc(1, 2)
self.assertEqual(len(w), 3)
# Type inference failure
self.assertEqual(w[0].category, NumbaWarning)
self.assertIn('type inference', str(w[0].message))
# Object mode
self.assertEqual(w[1].category, NumbaWarning)
self.assertIn('object mode', str(w[1].message))
# check objmode deprecation warning
self.check_objmode_deprecation_warning(w[2])
def test_return_type_warning(self):
y = np.ones(4, dtype=np.float32)
def return_external_array():
return y
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always', NumbaWarning)
ignore_internal_warnings()
cfunc = jit(_nrt=False)(return_external_array)
cfunc()
self.assertEqual(len(w), 3)
# Legal return value failure
self.assertEqual(w[0].category, NumbaWarning)
self.assertIn('return type', str(w[0].message))
# Object mode fall-back
self.assertEqual(w[1].category, NumbaWarning)
self.assertIn('object mode without forceobj=True',
str(w[1].message))
# check objmode deprecation warning
self.check_objmode_deprecation_warning(w[2])
def test_return_type_warning_with_nrt(self):
"""
Rerun test_return_type_warning with nrt
"""
y = np.ones(4, dtype=np.float32)
def return_external_array():
return y
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always', NumbaWarning)
ignore_internal_warnings()
cfunc = jit(return_external_array)
cfunc()
# No more warning
self.assertEqual(len(w), 0)
def test_no_warning_with_forceobj(self):
def add(x, y):
a = [] # noqa dead
return x + y
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always', NumbaWarning)
ignore_internal_warnings()
cfunc = jit(add, forceobj=True)
cfunc(1, 2)
self.assertEqual(len(w), 0)
def test_loop_lift_warn(self):
def do_loop(x):
a = {} # noqa dead
for i in range(x.shape[0]):
x[i] *= 2
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always', NumbaWarning)
ignore_internal_warnings()
x = np.ones(4, dtype=np.float32)
cfunc = jit(do_loop)
cfunc(x)
msg = '\n'.join(f"----------\n{x.message}" for x in w)
self.assertEqual(len(w), 4, msg=msg)
# Type inference failure (1st pass, in npm, fall-back to objmode
# with looplift)
self.assertEqual(w[0].category, NumbaWarning)
self.assertIn('type inference', str(w[0].message))
self.assertIn('WITH looplifting', str(w[0].message))
# Type inference failure (2nd pass, objmode with lifted loops,
# loop found but still failed, fall back to objmode no looplift)
self.assertEqual(w[1].category, NumbaWarning)
self.assertIn('type inference', str(w[1].message))
self.assertIn('WITHOUT looplifting', str(w[1].message))
# States compilation outcome
self.assertEqual(w[2].category, NumbaWarning)
self.assertIn('compiled in object mode without forceobj=True',
str(w[2].message))
self.assertIn('but has lifted loops', str(w[2].message))
# check objmode deprecation warning
self.check_objmode_deprecation_warning(w[3])
def test_deprecated(self):
@deprecated('foo')
def bar():
pass
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
ignore_internal_warnings()
bar()
self.assertEqual(len(w), 1)
self.assertEqual(w[0].category, DeprecationWarning)
self.assertIn('bar', str(w[0].message))
self.assertIn('foo', str(w[0].message))
def test_warnings_fixer(self):
# For some context, see #4083
wfix = errors.WarningsFixer(errors.NumbaWarning)
with wfix.catch_warnings('foo', 10):
warnings.warn(errors.NumbaWarning('same'))
warnings.warn(errors.NumbaDeprecationWarning('same'))
ignore_internal_warnings()
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
ignore_internal_warnings()
wfix.flush()
self.assertEqual(len(w), 2)
# the order of these will be backwards to the above, the
# WarningsFixer flush method sorts with a key based on str
# comparison
self.assertEqual(w[0].category, NumbaDeprecationWarning)
self.assertEqual(w[1].category, NumbaWarning)
self.assertIn('same', str(w[0].message))
self.assertIn('same', str(w[1].message))
def test_disable_performance_warnings(self):
not_found_ret_code = 55
found_ret_code = 99
expected = "'parallel=True' was specified but no transformation"
# NOTE: the error_usecases is needed as the NumbaPerformanceWarning's
# for parallel=True failing to parallelise do not appear for functions
# defined by string eval/exec etc.
parallel_code = """if 1:
import warnings
from numba.tests.error_usecases import foo
import numba
from numba.tests.support import ignore_internal_warnings
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
ignore_internal_warnings()
foo()
for x in w:
if x.category == numba.errors.NumbaPerformanceWarning:
if "%s" in str(x.message):
exit(%s)
exit(%s)
""" % (expected, found_ret_code, not_found_ret_code)
# run in the standard env, warning should raise
popen = subprocess.Popen([sys.executable, "-c", parallel_code])
out, err = popen.communicate()
self.assertEqual(popen.returncode, found_ret_code)
# run in an env with performance warnings disabled, should not warn
env = dict(os.environ)
env['NUMBA_DISABLE_PERFORMANCE_WARNINGS'] = "1"
popen = subprocess.Popen([sys.executable, "-c", parallel_code], env=env)
out, err = popen.communicate()
self.assertEqual(popen.returncode, not_found_ret_code)
if __name__ == '__main__':
unittest.main()