From 9ab12dcbce38a06f16e54c82f00baef4e5e9f7dc Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Mon, 4 Nov 2024 01:11:20 +0000 Subject: [PATCH 1/2] TST: Multiple modules in single pyf for gh-27622 --- .../upcoming_changes/27695.improvement.rst | 5 +++++ numpy/f2py/tests/src/regression/datonly.f90 | 17 +++++++++++++++++ numpy/f2py/tests/test_regression.py | 12 ++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 doc/release/upcoming_changes/27695.improvement.rst create mode 100644 numpy/f2py/tests/src/regression/datonly.f90 diff --git a/doc/release/upcoming_changes/27695.improvement.rst b/doc/release/upcoming_changes/27695.improvement.rst new file mode 100644 index 000000000000..95584b6e90ce --- /dev/null +++ b/doc/release/upcoming_changes/27695.improvement.rst @@ -0,0 +1,5 @@ +``f2py`` handles multiple modules and exposes variables again +------------------------------------------------------------- +A regression has been fixed which allows F2PY users to expose variables to +Python in modules with only assignments, and also fixes situations where +multiple modules are present within a single source file. diff --git a/numpy/f2py/tests/src/regression/datonly.f90 b/numpy/f2py/tests/src/regression/datonly.f90 new file mode 100644 index 000000000000..67fc4aca82e3 --- /dev/null +++ b/numpy/f2py/tests/src/regression/datonly.f90 @@ -0,0 +1,17 @@ +module datonly + implicit none + integer, parameter :: max_value = 100 + real, dimension(:), allocatable :: data_array +end module datonly + +module dat + implicit none + integer, parameter :: max_= 1009 +end module dat + +subroutine simple_subroutine(ain, aout) + use dat, only: max_ + integer, intent(in) :: ain + integer, intent(out) :: aout + aout = ain + max_ +end subroutine simple_subroutine diff --git a/numpy/f2py/tests/test_regression.py b/numpy/f2py/tests/test_regression.py index e11ed1a0efa3..cbc81508ae42 100644 --- a/numpy/f2py/tests/test_regression.py +++ b/numpy/f2py/tests/test_regression.py @@ -24,6 +24,18 @@ def test_inout(self): assert np.allclose(x, [3, 1, 2]) +class TestDataOnlyMultiModule(util.F2PyTest): + # Check that modules without subroutines work + sources = [util.getpath("tests", "src", "regression", "datonly.f90")] + + @pytest.mark.slow + def test_mdat(self): + assert self.module.datonly.max_value == 100 + assert self.module.dat.max_ == 1009 + int_in = 5 + assert self.module.simple_subroutine(5) == 1014 + + class TestNegativeBounds(util.F2PyTest): # Check that negative bounds work correctly sources = [util.getpath("tests", "src", "negative_bounds", "issue_20853.f90")] From 690e663a1d0a526bb4b2b08b3ed9d1ee171c97f2 Mon Sep 17 00:00:00 2001 From: Rohit Goswami Date: Mon, 4 Nov 2024 01:45:13 +0000 Subject: [PATCH 2/2] BUG: Handle multi-module files and common better Fixes gh-25186 gh-25337 gh-27622 --- numpy/f2py/auxfuncs.py | 2 +- numpy/f2py/f90mod_rules.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/numpy/f2py/auxfuncs.py b/numpy/f2py/auxfuncs.py index 88a9ff552343..77cf6ee2b167 100644 --- a/numpy/f2py/auxfuncs.py +++ b/numpy/f2py/auxfuncs.py @@ -44,7 +44,7 @@ 'isunsigned_long_long', 'isunsigned_long_longarray', 'isunsigned_short', 'isunsigned_shortarray', 'l_and', 'l_not', 'l_or', 'outmess', 'replace', 'show', 'stripcomma', 'throw_error', 'isattr_value', 'getuseblocks', - 'process_f2cmap_dict' + 'process_f2cmap_dict', 'containscommon' ] diff --git a/numpy/f2py/f90mod_rules.py b/numpy/f2py/f90mod_rules.py index 9c52938f08da..b1cd15320657 100644 --- a/numpy/f2py/f90mod_rules.py +++ b/numpy/f2py/f90mod_rules.py @@ -97,9 +97,6 @@ def dadd(line, s=doc): usenames = getuseblocks(pymod) for m in findf90modules(pymod): - contains_functions_or_subroutines = any( - item for item in m["body"] if item["block"] in ["function", "subroutine"] - ) sargs, fargs, efargs, modobjs, notvars, onlyvars = [], [], [], [], [ m['name']], [] sargsp = [] @@ -120,8 +117,9 @@ def dadd(line, s=doc): outmess(f"\t\t\tSkipping {m['name']} since there are no public vars/func in this module...\n") continue - if m['name'] in usenames and not contains_functions_or_subroutines: - outmess(f"\t\t\tSkipping {m['name']} since it is in 'use'...\n") + # gh-25186 + if m['name'] in usenames and containscommon(m): + outmess(f"\t\t\tSkipping {m['name']} since it is in 'use' and contains a common block...\n") continue if onlyvars: outmess('\t\t Variables: %s\n' % (' '.join(onlyvars)))