diff --git a/doc/release/upcoming_changes/21807.improvement.rst b/doc/release/upcoming_changes/21807.improvement.rst new file mode 100644 index 000000000000..c0043cad3603 --- /dev/null +++ b/doc/release/upcoming_changes/21807.improvement.rst @@ -0,0 +1,7 @@ +F2PY supports the value attribute +================================= + +The Fortran standard requires that variables declared with the ``value`` +attribute must be passed by value instead of reference. F2PY now supports this +use pattern correctly. So ``integer, intent(in), value :: x`` in Fortran codes +will have correct wrappers generated. diff --git a/numpy/f2py/auxfuncs.py b/numpy/f2py/auxfuncs.py index 98c9eff78fc9..3f9b0ceafa21 100644 --- a/numpy/f2py/auxfuncs.py +++ b/numpy/f2py/auxfuncs.py @@ -47,7 +47,7 @@ 'isunsigned_chararray', 'isunsigned_long_long', 'isunsigned_long_longarray', 'isunsigned_short', 'isunsigned_shortarray', 'l_and', 'l_not', 'l_or', 'outmess', - 'replace', 'show', 'stripcomma', 'throw_error', + 'replace', 'show', 'stripcomma', 'throw_error', 'isattr_value' ] @@ -298,6 +298,9 @@ def issubroutine_wrap(rout): return 0 return issubroutine(rout) and hasassumedshape(rout) +def isattr_value(var): + return 'value' in var.get('attrspec', []) + def hasassumedshape(rout): if rout.get('hasassumedshape'): @@ -692,7 +695,8 @@ def getcallprotoargument(rout, cb_map={}): elif isstring(var): pass else: - ctype = ctype + '*' + if not isattr_value(var): + ctype = ctype + '*' if ((isstring(var) or isarrayofstrings(var) # obsolete? or isstringarray(var))): diff --git a/numpy/f2py/rules.py b/numpy/f2py/rules.py index 63c48a8782c9..1bac871024d8 100755 --- a/numpy/f2py/rules.py +++ b/numpy/f2py/rules.py @@ -71,7 +71,7 @@ islong_double, islong_doublefunction, islong_long, islong_longfunction, ismoduleroutine, isoptional, isrequired, isscalar, issigned_long_longarray, isstring, isstringarray, - isstringfunction, issubroutine, + isstringfunction, issubroutine, isattr_value, issubroutine_wrap, isthreadsafe, isunsigned, isunsigned_char, isunsigned_chararray, isunsigned_long_long, isunsigned_long_longarray, isunsigned_short, isunsigned_shortarray, @@ -874,7 +874,7 @@ { # Common 'decl': ' #ctype# #varname# = 0;', 'pyobjfrom': {debugcapi: ' fprintf(stderr,"#vardebugshowvalue#\\n",#varname#);'}, - 'callfortran': {isintent_c: '#varname#,', l_not(isintent_c): '&#varname#,'}, + 'callfortran': {l_or(isintent_c, isattr_value): '#varname#,', l_not(l_or(isintent_c, isattr_value)): '&#varname#,'}, 'return': {isintent_out: ',#varname#'}, '_check': l_and(isscalar, l_not(iscomplex)) }, { diff --git a/numpy/f2py/tests/src/value_attrspec/gh21665.f90 b/numpy/f2py/tests/src/value_attrspec/gh21665.f90 new file mode 100644 index 000000000000..7d9dc0fd4acb --- /dev/null +++ b/numpy/f2py/tests/src/value_attrspec/gh21665.f90 @@ -0,0 +1,9 @@ +module fortfuncs + implicit none +contains + subroutine square(x,y) + integer, intent(in), value :: x + integer, intent(out) :: y + y = x*x + end subroutine square +end module fortfuncs diff --git a/numpy/f2py/tests/test_value_attrspec.py b/numpy/f2py/tests/test_value_attrspec.py new file mode 100644 index 000000000000..83aaf6c9161e --- /dev/null +++ b/numpy/f2py/tests/test_value_attrspec.py @@ -0,0 +1,14 @@ +import os +import pytest + +from . import util + +class TestValueAttr(util.F2PyTest): + sources = [util.getpath("tests", "src", "value_attrspec", "gh21665.f90")] + + # gh-21665 + def test_long_long_map(self): + inp = 2 + out = self.module.fortfuncs.square(inp) + exp_out = 4 + assert out == exp_out