Skip to content

Commit f3d48d0

Browse files
authored
Merge pull request numpy#27049 from m-weigand/gh26920
BUG: f2py: better handle filtering of public/private subroutines
2 parents 99dd094 + d442a61 commit f3d48d0

File tree

5 files changed

+97
-11
lines changed

5 files changed

+97
-11
lines changed

numpy/f2py/auxfuncs.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,15 @@
3636
'isintent_nothide', 'isintent_out', 'isintent_overwrite', 'islogical',
3737
'islogicalfunction', 'islong_complex', 'islong_double',
3838
'islong_doublefunction', 'islong_long', 'islong_longfunction',
39-
'ismodule', 'ismoduleroutine', 'isoptional', 'isprivate', 'isrequired',
40-
'isroutine', 'isscalar', 'issigned_long_longarray', 'isstring',
41-
'isstringarray', 'isstring_or_stringarray', 'isstringfunction',
42-
'issubroutine', 'get_f2py_modulename',
43-
'issubroutine_wrap', 'isthreadsafe', 'isunsigned', 'isunsigned_char',
44-
'isunsigned_chararray', 'isunsigned_long_long',
45-
'isunsigned_long_longarray', 'isunsigned_short',
46-
'isunsigned_shortarray', 'l_and', 'l_not', 'l_or', 'outmess',
47-
'replace', 'show', 'stripcomma', 'throw_error', 'isattr_value',
48-
'getuseblocks', 'process_f2cmap_dict'
39+
'ismodule', 'ismoduleroutine', 'isoptional', 'isprivate', 'isvariable',
40+
'isrequired', 'isroutine', 'isscalar', 'issigned_long_longarray',
41+
'isstring', 'isstringarray', 'isstring_or_stringarray', 'isstringfunction',
42+
'issubroutine', 'get_f2py_modulename', 'issubroutine_wrap', 'isthreadsafe',
43+
'isunsigned', 'isunsigned_char', 'isunsigned_chararray',
44+
'isunsigned_long_long', 'isunsigned_long_longarray', 'isunsigned_short',
45+
'isunsigned_shortarray', 'l_and', 'l_not', 'l_or', 'outmess', 'replace',
46+
'show', 'stripcomma', 'throw_error', 'isattr_value', 'getuseblocks',
47+
'process_f2cmap_dict'
4948
]
5049

5150

@@ -518,6 +517,15 @@ def isprivate(var):
518517
return 'attrspec' in var and 'private' in var['attrspec']
519518

520519

520+
def isvariable(var):
521+
# heuristic to find public/private declarations of filtered subroutines
522+
if len(var) == 1 and 'attrspec' in var and \
523+
var['attrspec'][0] in ('public', 'private'):
524+
is_var = False
525+
else:
526+
is_var = True
527+
return is_var
528+
521529
def hasinitvalue(var):
522530
return '=' in var
523531

numpy/f2py/f90mod_rules.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,16 @@ def dadd(line, s=doc):
110110
notvars.append(b['name'])
111111
for n in m['vars'].keys():
112112
var = m['vars'][n]
113-
if (n not in notvars) and (not l_or(isintent_hide, isprivate)(var)):
113+
114+
if (n not in notvars and isvariable(var)) and (not l_or(isintent_hide, isprivate)(var)):
114115
onlyvars.append(n)
115116
mfargs.append(n)
116117
outmess('\t\tConstructing F90 module support for "%s"...\n' %
117118
(m['name']))
119+
if len(onlyvars) == 0 and len(notvars) == 1 and m['name'] in notvars:
120+
outmess(f"\t\t\tSkipping {m['name']} since there are no public vars/func in this module...\n")
121+
continue
122+
118123
if m['name'] in usenames and not contains_functions_or_subroutines:
119124
outmess(f"\t\t\tSkipping {m['name']} since it is in 'use'...\n")
120125
continue
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module mod2
2+
implicit none
3+
private mod2_func1
4+
contains
5+
6+
subroutine mod2_func1()
7+
print*, "mod2_func1"
8+
end subroutine mod2_func1
9+
10+
end module mod2
11+
12+
module mod1
13+
implicit none
14+
private :: mod1_func1
15+
contains
16+
17+
subroutine mod1_func1()
18+
print*, "mod1_func1"
19+
end subroutine mod1_func1
20+
21+
end module mod1
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module mod2
2+
implicit none
3+
PUBLIC :: mod2_func1
4+
contains
5+
6+
subroutine mod2_func1()
7+
print*, "mod2_func1"
8+
end subroutine mod2_func1
9+
10+
end module mod2
11+
12+
module mod1
13+
implicit none
14+
PUBLIC :: mod1_func1
15+
contains
16+
17+
subroutine mod1_func1()
18+
print*, "mod1_func1"
19+
end subroutine mod1_func1
20+
21+
end module mod1

numpy/f2py/tests/test_modules.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,37 @@
55
from numpy.testing import IS_PYPY
66

77

8+
@pytest.mark.slow
9+
class TestModuleFilterPublicEntities(util.F2PyTest):
10+
sources = [
11+
util.getpath(
12+
"tests", "src", "modules", "gh26920",
13+
"two_mods_with_one_public_routine.f90"
14+
)
15+
]
16+
# we filter the only public function mod2
17+
only = ["mod1_func1", ]
18+
19+
def test_gh26920(self):
20+
# if it compiles and can be loaded, things are fine
21+
pass
22+
23+
24+
@pytest.mark.slow
25+
class TestModuleWithoutPublicEntities(util.F2PyTest):
26+
sources = [
27+
util.getpath(
28+
"tests", "src", "modules", "gh26920",
29+
"two_mods_with_no_public_entities.f90"
30+
)
31+
]
32+
only = ["mod1_func1", ]
33+
34+
def test_gh26920(self):
35+
# if it compiles and can be loaded, things are fine
36+
pass
37+
38+
839
@pytest.mark.slow
940
class TestModuleDocString(util.F2PyTest):
1041
sources = [util.getpath("tests", "src", "modules", "module_data_docstring.f90")]

0 commit comments

Comments
 (0)