Repository: numpy/numpy
Pull request: BUG: add endfunction, endsubroutine to valid fortran end words
Test, fix for missing end words in fortran subroutines and functions.
Fixes gh-14625

Problem statement:
BUG: f2py endfunction and endsubroutine parsing
<!-- Please describe the issue in detail here, and fill in the fields below -->

Hi, while testing f2py for some simple examples i stumbled upon an error, which i think i have understood the cause, but i didn't go through the f2py implementation to propose a solution.

I believe the fortran file is not parsed correctly when generating the signature file if functions and subroutines are closed with `endfunction` or `endsubroutine`, without space, which is allowed in the gfortran and compiles correctly.

the code test.f90 builds correctly with either `end subroutine` or `endsubroutine`

but in the latter case, the python module loads the first subroutine (`subb`) but not the second (`subc`)
 
### Reproducing code example:

<!-- A short code example that reproduces the problem/missing feature. It should be
self-contained, i.e., possible to run as-is via 'python myproblem.py' -->

`test.f90`:
```fortran
module test
implicit none
contains
subroutine subb(k)
  real(8), intent(inout) :: k(:)
  k=k+1
endsubroutine
subroutine subc(w,k)
  real(8), intent(in) :: w(:)
  real(8), intent(out) :: k(size(w))
  k=w+1
endsubroutine
end module
```
`build.py`:
```python
src = open('test.f90','r').read()
from numpy import f2py
f2py.compile(source=src,source_fn='test1854.tmp.f90',extension='.f90',extra_args="--opt='-ffree-line-length-none'",modulename='test1854')
import test1854                                                                                                                                                 
print( test1854.test.subb.__doc__ )
import numpy as np
x=np.array([1,2,3],dtype=np.float)
print( test1854.test.subb(x))
print(x)
print( test1854.test.subc.__doc__ )
print( test1854.test.subc(x))
print(x)
```

<!-- Remove these sections for a feature request -->

### Error message:

<!-- Full error message, if any (starting from line Traceback: ...) -->

`python build.py`:
```
Traceback (most recent call last):
  File "build.py", line 10, in <module>
    print( test1854.test.subc.__doc__ )
AttributeError: 'fortran' object has no attribute 'subc'
```

### Numpy/Python version information:

<!-- Output from 'import sys, numpy; print(numpy.__version__, sys.version)' -->
```
1.16.4 3.6.9 |Anaconda, Inc.| (default, Jul 30 2019, 19:07:31) 
[GCC 7.3.0]
```

Hints from issue discussion:
@iurisegtovich, thanks for reporting the issue and supplying the code to reproduce the problem.  It  occurs in the development version, too.
As a fix, add `endfunction` and `endsubroutine` to the pattern defined in
https://github.com/numpy/numpy/blob/9ed9806637297aa3915294ce1ae97184529dbd04/numpy/f2py/crackfortran.py#L561

Commit message: DOC: fix outstanding typo in NumPy version

Patch to review:
diff --git a/numpy/f2py/cfuncs.py b/numpy/f2py/cfuncs.py
index ccb7b3a329f7..cede0611950a 100644
--- a/numpy/f2py/cfuncs.py
+++ b/numpy/f2py/cfuncs.py
@@ -542,7 +542,7 @@
     'ARRSIZE'] = '#define ARRSIZE(dims,rank) (_PyArray_multiply_list(dims,rank))'
 cppmacros['OLDPYNUM'] = """\
 #ifdef OLDPYNUM
-#error You need to install NumPy version 13 or higher. See https://scipy.org/install.html
+#error You need to install NumPy version 0.13 or higher. See https://scipy.org/install.html
 #endif
 """
 ################# C functions ###############
diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py
index 2aaf5d7c6c12..2db4a47e86ca 100755
--- a/numpy/f2py/crackfortran.py
+++ b/numpy/f2py/crackfortran.py
@@ -558,7 +558,8 @@ def readfortrancode(ffile, dowithline=show, istop=1):
     r'|module(?!\s*procedure)|python\s*module|interface|type(?!\s*\()'
 beginpattern90 = re.compile(
     beforethisafter % ('', groupbegins90, groupbegins90, '.*'), re.I), 'begin'
-groupends = r'end|endprogram|endblockdata|endmodule|endpythonmodule|endinterface'
+groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|'
+             r'endinterface|endsubroutine|endfunction')
 endpattern = re.compile(
     beforethisafter % ('', groupends, groupends, r'[\w\s]*'), re.I), 'end'
 # endifs='end\s*(if|do|where|select|while|forall)'
diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py
new file mode 100644
index 000000000000..8af1b4d80b78
--- /dev/null
+++ b/numpy/f2py/tests/test_crackfortran.py
@@ -0,0 +1,39 @@
+from __future__ import division, absolute_import, print_function
+
+import pytest
+
+import numpy as np
+from numpy.testing import assert_array_equal
+from . import util
+
+
+class TestNoSpace(util.F2PyTest):
+    code = """
+        subroutine subb(k)
+          real(8), intent(inout) :: k(:)
+          k=k+1
+        endsubroutine
+
+        subroutine subc(w,k)
+          real(8), intent(in) :: w(:)
+          real(8), intent(out) :: k(size(w))
+          k=w+1
+        endsubroutine
+
+        function t0(value)
+          character value
+          character t0
+          t0 = value
+        endfunction
+ 
+    """
+
+    def test_module(self):
+        k = np.array([1, 2, 3], dtype = np.float)
+        w = np.array([1, 2, 3], dtype = np.float)
+        self.module.subb(k)
+        assert_array_equal(k, w + 1)
+        self.module.subc([w, k])
+        assert_array_equal(k, w + 1)
+        assert self.module.t0(23) == b'2'
+


Code review comments:
diff_hunk: @@ -0,0 +1,39 @@
+from __future__ import division, absolute_import, print_function
+
+import pytest
+
+import numpy as np
+from numpy.testing import assert_array_equal
+from . import util
+
+
+class TestNoSpace(util.F2PyTest):
+    code = """
+        subroutine subb(k)
+          real(8), intent(inout) :: k(:)
+          k=k+1
+        endsubroutine
+
+        subroutine subc(w,k)
+          real(8), intent(in) :: w(:)
+          real(8), intent(out) :: k(size(w))
+          k=w+1
+        endsubroutine
+
+        function t0(value)
+          character value
+          character t0
+          t0 = value
+        endfunction
+ 
+    """
+
+    def test_module(self):
+        k = np.array([1, 2, 3], dtype = np.float)
+        w = np.array([1, 2, 3], dtype = np.float)
path: numpy/f2py/tests/test_crackfortran.py
text: @user1:
Let's not make #6103 worse ;)
```suggestion
        w = np.array([1, 2, 3], dtype=float)
```

@author:
heh, fixing