diff --git a/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py b/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py index 77b60b639febb..d014f6eb664e3 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_numbadeclare.py @@ -265,15 +265,22 @@ def pywrapper({SIGNATURE}): # Execute the pywrapper code and generate the wrapper function # which calls the jitted C function - exec(pywrappercode, glob, locals()) in {} - - if not 'pywrapper' in locals(): + # Python 3.13 changes the semantics of the `locals()` builtin function + # such that in optimized scopes (e.g. at function scope as it is + # happening right now) the dictionary is not updated when changed. Bind + # the `locals()` dictionary to a temporary object. This way, the call + # to `exec()` will actually change the dictionary and the pywrapper + # function will be found. Note that this change is backwards-compatible. + local_objects = locals() + exec(pywrappercode, glob, local_objects) + + if not 'pywrapper' in local_objects: raise Exception('Failed to create Python wrapper function:\n{}'.format(pywrappercode)) # Jit the Python wrapper code c_return_type, c_input_types = get_c_signature(input_types, return_type) try: - nbcfunc = nb.cfunc(c_return_type(*c_input_types), nopython=True)(locals()['pywrapper']) + nbcfunc = nb.cfunc(c_return_type(*c_input_types), nopython=True)(local_objects['pywrapper']) except: raise Exception('Failed to jit Python wrapper with numba.cfunc') func.__py_wrapper__ = pywrappercode diff --git a/bindings/pyroot/pythonizations/test/CMakeLists.txt b/bindings/pyroot/pythonizations/test/CMakeLists.txt index 8533270a10e9b..78e219843b14a 100644 --- a/bindings/pyroot/pythonizations/test/CMakeLists.txt +++ b/bindings/pyroot/pythonizations/test/CMakeLists.txt @@ -174,12 +174,10 @@ if (dataframe) # std::string_view in CPyCppyy ROOT_ADD_PYUNITTEST(pyroot_string_view string_view.py) if(NOT MSVC OR win_broken_tests) - if(NOT DEFINED ENV{ROOTTEST_IGNORE_NUMBA_PY3}) - # Test wrapping Python callables for use in C++ using numba - ROOT_ADD_PYUNITTEST(pyroot_numbadeclare numbadeclare.py PYTHON_DEPS numba) - ROOT_ADD_PYUNITTEST(pyroot_rdf_filter_pyz rdf_filter_pyz.py PYTHON_DEPS numba) - ROOT_ADD_PYUNITTEST(pyroot_rdf_define_pyz rdf_define_pyz.py PYTHON_DEPS numba) - endif() + # Test wrapping Python callables for use in C++ using numba + ROOT_ADD_PYUNITTEST(pyroot_numbadeclare numbadeclare.py PYTHON_DEPS numba) + ROOT_ADD_PYUNITTEST(pyroot_rdf_filter_pyz rdf_filter_pyz.py PYTHON_DEPS numba) + ROOT_ADD_PYUNITTEST(pyroot_rdf_define_pyz rdf_define_pyz.py PYTHON_DEPS numba) endif() endif() diff --git a/bindings/pyroot/pythonizations/test/numbadeclare.py b/bindings/pyroot/pythonizations/test/numbadeclare.py index 0a380523f3ada..b46634b658c20 100644 --- a/bindings/pyroot/pythonizations/test/numbadeclare.py +++ b/bindings/pyroot/pythonizations/test/numbadeclare.py @@ -1,20 +1,10 @@ import unittest import ROOT import sys -import os import numpy as np import gc - -# Check whether these tests should be skipped -skip = False -skip_reason = "" -if "ROOTTEST_IGNORE_NUMBA_PY3" in os.environ: - skip = True - skip_reason = "Running python3 and ROOTTEST_IGNORE_NUMBA_PY3 was set" - -if not skip: - import numba as nb +import numba as nb default_test_inputs = [-1.0, 0.0, 100.0] @@ -28,7 +18,6 @@ class NumbaDeclareSimple(unittest.TestCase): # Test refcounts - @unittest.skipIf(skip, skip_reason) def test_refcount_decorator(self): """ Test refcount of decorator @@ -37,7 +26,6 @@ def test_refcount_decorator(self): gc.collect() self.assertEqual(sys.getrefcount(x), 2) - @unittest.skipIf(skip, skip_reason) def test_refcount_pycallable(self): """ Test refcount of decorated callable @@ -52,7 +40,6 @@ def f2(x): self.assertEqual(sys.getrefcount(f1), sys.getrefcount(f2) + 2) # Test optional name - @unittest.skipIf(skip, skip_reason) def test_optional_name(self): """ Test optional name of wrapper function @@ -64,7 +51,6 @@ def f(x): self.assertTrue(hasattr(ROOT.Numba, optname)) # Test attributes - @unittest.skipIf(skip, skip_reason) def test_additional_attributes(self): """ Test additional attributes @@ -87,7 +73,6 @@ def fn1(x): self.assertEqual(sys.getrefcount(fn1.numba_func), 3) # Test cling integration - @unittest.skipIf(skip, skip_reason) def test_cling(self): """ Test function call in cling @@ -99,7 +84,6 @@ def fn12(x): self.assertEqual(fn12(42.0), ROOT.y12) # Test RDataFrame integration - @unittest.skipIf(skip, skip_reason) def test_rdataframe(self): """ Test function call as part of RDataFrame @@ -113,7 +97,6 @@ def fn13(x): self.assertEqual(mean_x.GetValue(), 1.5) self.assertEqual(mean_y.GetValue(), 3.0) - @unittest.skipIf(skip, skip_reason) def test_rdataframe_temporary(self): """ Test passing a temporary from an RDataFrame operation @@ -131,7 +114,6 @@ def pass_temporary(v): self.assertTrue(np.array_equal(rvecf, np.array([4.]))) # Test wrappings - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_void(self): """ Test wrapper with different input/output configurations @@ -144,7 +126,6 @@ def fn2n(): self.assertEqual(x1, x2) self.assertEqual(type(x1), type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_f(self): """ Test wrapper with different input/output configurations @@ -158,7 +139,6 @@ def fn2(x): self.assertEqual(x1, x2) self.assertEqual(type(x1), type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_d(self): """ Test wrapper with different input/output configurations @@ -173,7 +153,6 @@ def fn2d(x): # NOTE: There is no double in Python because everything is a double. self.assertEqual(type(x1), type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_i(self): """ Test wrapper with different input/output configurations @@ -187,7 +166,6 @@ def fn3(x): self.assertEqual(x1, x2) self.assertEqual(type(x1), type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_l(self): """ Test wrapper with different input/output configurations @@ -201,7 +179,6 @@ def fn4(x): self.assertEqual(x1, x2) self.assertEqual(int, type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_u(self): """ Test wrapper with different input/output configurations @@ -215,7 +192,6 @@ def fn5(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), int) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_k(self): """ Test wrapper with different input/output configurations @@ -229,7 +205,6 @@ def fn6(x): self.assertEqual(x1, x2) self.assertEqual(int, type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_b(self): """ Test wrapper with different input/output configurations @@ -243,7 +218,6 @@ def fn6b(x): self.assertEqual(x1, x2) self.assertEqual(type(x1), type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_b(self): """ Test wrapper with different input/output configurations @@ -257,7 +231,6 @@ def fn6b2(x): self.assertEqual(x1, x2) self.assertEqual(type(x1), type(x2)) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_i(self): """ Test wrapper with different input/output configurations @@ -270,7 +243,6 @@ def fn7i(x): x2 = ROOT.Numba.fn7i(v) self.assertEqual(x1, x2) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_l(self): """ Test wrapper with different input/output configurations @@ -283,7 +255,6 @@ def fn7l(x): x2 = ROOT.Numba.fn7l(v) self.assertEqual(x1, x2) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_ui(self): """ Test wrapper with different input/output configurations @@ -296,7 +267,6 @@ def fn7ui(x): x2 = ROOT.Numba.fn7ui(v) self.assertEqual(x1, x2) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_ul(self): """ Test wrapper with different input/output configurations @@ -322,7 +292,6 @@ class NumbaDeclareArray(unittest.TestCase): # Preload the library now. ROOT.gSystem.Load("libROOTVecOps") - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecf(self): """ Test wrapper with different input/output configurations @@ -337,7 +306,6 @@ def g1(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), float) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecf_vecd(self): """ Test wrapper with different input/output configurations @@ -352,7 +320,6 @@ def g1_2vec(x, y): self.assertEqual(x1, x2) self.assertEqual(type(x2), float) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecd(self): """ Test wrapper with different input/output configurations @@ -367,7 +334,6 @@ def g1d(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), float) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_veci(self): """ Test wrapper with different input/output configurations @@ -382,7 +348,6 @@ def g1i(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), int) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecl(self): """ Test wrapper with different input/output configurations @@ -397,7 +362,6 @@ def g1l(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), int) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecui(self): """ Test wrapper with different input/output configurations @@ -412,7 +376,6 @@ def g1ui(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), int) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecul(self): """ Test wrapper with different input/output configurations @@ -427,7 +390,6 @@ def g1ul(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), int) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecb(self): """ Test wrapper with different input/output configurations @@ -442,7 +404,6 @@ def g1b(x): self.assertEqual(x1, x2) self.assertEqual(type(x2), int) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_vecf(self): """ Test wrapper with different input/output configurations @@ -456,7 +417,6 @@ def g2f(x): x2 = ROOT.Numba.g2f(ROOT.VecOps.RVec('float')(v)) self.assertTrue((x1 == x2).all()) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_vecd(self): """ Test wrapper with different input/output configurations @@ -470,7 +430,6 @@ def g2d(x): x2 = ROOT.Numba.g2d(ROOT.VecOps.RVec('double')(v)) self.assertTrue((x1 == x2).all()) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_veci(self): """ Test wrapper with different input/output configurations @@ -484,7 +443,6 @@ def g2i(x): x2 = ROOT.Numba.g2i(ROOT.VecOps.RVec('int')(v)) self.assertTrue((x1 == x2).all()) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_vecl(self): """ Test wrapper with different input/output configurations @@ -498,7 +456,6 @@ def g2l(x): x2 = ROOT.Numba.g2l(ROOT.VecOps.RVec('long')(v)) self.assertTrue((x1 == x2).all()) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_vecul(self): """ Test wrapper with different input/output configurations @@ -512,7 +469,6 @@ def g2ul(x): x2 = ROOT.Numba.g2ul(ROOT.VecOps.RVec('unsigned long')(v)) self.assertTrue((x1 == x2).all()) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_vecui(self): """ Test wrapper with different input/output configurations @@ -526,7 +482,6 @@ def g2ui(x): x2 = ROOT.Numba.g2ui(ROOT.VecOps.RVec('unsigned int')(v)) self.assertTrue((x1 == x2).all()) - @unittest.skipIf(skip, skip_reason) def test_wrapper_out_vecb(self): """ Test wrapper with different input/output configurations @@ -541,7 +496,6 @@ def g2b(x): self.assertEqual(x1[0], bool(x2[0])) self.assertEqual(x1[1], bool(x2[1])) - @unittest.skipIf(skip, skip_reason) def test_wrapper_in_vecfb_out_vecf(self): """ Test wrapper with different input/output configurations @@ -556,7 +510,6 @@ def g2fb(x, y): self.assertEqual(x1[0], bool(x2[0])) self.assertEqual(x1[1], bool(x2[1])) - @unittest.skipIf(skip, skip_reason) def test_const_modifier(self): """ Test const modifier in input argument type @@ -569,7 +522,6 @@ def const_mod(v): self.assertTrue(np.array_equal(rvecf, np.array([1.,4.]))) - @unittest.skipIf(skip, skip_reason) def test_reference(self): """ Test passing a reference as input argument diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index eb60bf24f53e7..9582c14a5824d 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -784,15 +784,10 @@ if(ROOT_pyroot_FOUND) ) if(NOT dataframe - OR DEFINED ENV{ROOTTEST_IGNORE_NUMBA_PY3} OR (MSVC AND NOT win_broken_tests)) list(APPEND pyveto analysis/dataframe/df038_NumbaDeclare.py) endif() - if(dataframe AND DEFINED ENV{ROOTTEST_IGNORE_PANDAS_PY3}) - list(APPEND pyveto analysis/dataframe/df026_AsNumpyArrays.py) - endif() - # Rules specific to distributed RDataFrame # Disable distributed RDF tutorials if we didn't check dependencies in the environment first if(NOT test_distrdf_pyspark)