-
Notifications
You must be signed in to change notification settings - Fork 9
/
nonstopf2py.py
91 lines (76 loc) · 2.47 KB
/
nonstopf2py.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
An non-stop f2py. When the fortran program calls stop, it is trapped with a long jmp,
and the error is converted to a python error.
A lot of inspiration came from patch_f2py.py in James Kermode's quippy
(http://www.jrkermode.co.uk/quippy)
currently supported fortran runtimes are:
ifcore (intel)
gfortran (gnu)
For new runtimes, compile a small f90 that uses stop
boo.f90:
program boo
stop "boo the dog stops here"
end program boo
and
objdump -t a.o
to find the function to override.
-- Yu Feng <yfeng1@cmu.edu> @ McWilliam Center, Carnegie Mellon 2012
"""
from numpy import f2py
# trap the fortran STOP methods.
# luckily they are not builtin/inlined.
# we do not need to free 'message'. it appears to be staticly allocated
# by the compiler.
f2py.rules.module_rules['modulebody'] = f2py.rules.module_rules['modulebody'].replace(
'#includes0#\n',
r"""#includes0#
#include <setjmp.h>
static char * _error;
static jmp_buf _env;
void for_stop_core(char * message, int len) {
_error = strndup(message, len);
longjmp(_env, 1);
}
void _gfortran_stop_string(char * message, int len) {
_error = strndup(message, len);
longjmp(_env, 1);
}
""")
# here we fight the leak as f2py will no longer always return.
# the easiest way is to first construct the return tuple,
# then free them all
f2py.rules.routine_rules['body'] = f2py.rules.routine_rules['body'].replace(
"""\t\tif (f2py_success) {
#pyobjfrom#
/*end of pyobjfrom*/
\t\tCFUNCSMESS(\"Building return value.\\n\");
\t\tcapi_buildvalue = Py_BuildValue(\"#returnformat#\"#return#);
/*closepyobjfrom*/
#closepyobjfrom#
\t\t} /*if (f2py_success) after callfortranroutine*/""",
"""\t\t{
#pyobjfrom#
/*end of pyobjfrom*/
\t\tCFUNCSMESS(\"Building return value.\\n\");
\t\tcapi_buildvalue = Py_BuildValue(\"#returnformat#\"#return#);
/*closepyobjfrom*/
#closepyobjfrom#
\t\tif(!f2py_success) {
\t\t\tPy_XDECREF(capi_buildvalue);
\t\t\tcapi_buildvalue = NULL;
\t\t}
\t\t}
/*if (f2py_success) after callfortranroutine*/
"""
)
# the actual function call. free _error as PyErr_SetString will copy it.
f2py.rules.routine_rules['body'] = f2py.rules.routine_rules['body'].replace(
'#callfortranroutine#\n',
r"""
if(setjmp(_env)) {
PyErr_SetString(PyExc_RuntimeError, _error);
free(_error);
} else {
#callfortranroutine#
}
""")