summaryrefslogtreecommitdiff
path: root/numpy/f2py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/f2py')
-rwxr-xr-xnumpy/f2py/crackfortran.py85
-rw-r--r--numpy/f2py/src/fortranobject.c85
-rw-r--r--numpy/f2py/src/fortranobject.h26
-rw-r--r--numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c26
-rw-r--r--numpy/f2py/tests/test_quoted_character.py32
-rw-r--r--numpy/f2py/tests/test_semicolon_split.py53
6 files changed, 202 insertions, 105 deletions
diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py
index 78802ef07..dc560f98e 100755
--- a/numpy/f2py/crackfortran.py
+++ b/numpy/f2py/crackfortran.py
@@ -346,8 +346,6 @@ def readfortrancode(ffile, dowithline=show, istop=1):
cont = 0
finalline = ''
ll = ''
- commentline = re.compile(
- r'(?P<line>([^"]*["][^"]*["][^"!]*|[^\']*\'[^\']*\'[^\'!]*|[^!\'"]*))!{1}(?P<rest>.*)')
includeline = re.compile(
r'\s*include\s*(\'|")(?P<name>[^\'"]*)(\'|")', re.I)
cont1 = re.compile(r'(?P<line>.*)&\s*\Z')
@@ -391,17 +389,10 @@ def readfortrancode(ffile, dowithline=show, istop=1):
break
l = l[:-1]
if not strictf77:
- r = commentline.match(l)
- if r:
- l = r.group('line') + ' ' # Strip comments starting with `!'
- rl = r.group('rest')
- if rl[:4].lower() == 'f2py': # f2py directive
- l = l + 4 * ' '
- r = commentline.match(rl[4:])
- if r:
- l = l + r.group('line')
- else:
- l = l + rl[4:]
+ (l, rl) = split_by_unquoted(l, '!')
+ l += ' '
+ if rl[:5].lower() == '!f2py': # f2py directive
+ l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!')
if l.strip() == '': # Skip empty line
cont = 0
continue
@@ -618,6 +609,25 @@ multilinepattern = re.compile(
r"\s*(?P<before>''')(?P<this>.*?)(?P<after>''')\s*\Z", re.S), 'multiline'
##
+def split_by_unquoted(line, characters):
+ """
+ Splits the line into (line[:i], line[i:]),
+ where i is the index of first occurence of one of the characters
+ not within quotes, or len(line) if no such index exists
+ """
+ assert not (set('"\'') & set(characters)), "cannot split by unquoted quotes"
+ r = re.compile(
+ r"\A(?P<before>({single_quoted}|{double_quoted}|{not_quoted})*)"
+ r"(?P<after>{char}.*)\Z".format(
+ not_quoted="[^\"'{}]".format(re.escape(characters)),
+ char="[{}]".format(re.escape(characters)),
+ single_quoted=r"('([^'\\]|(\\.))*')",
+ double_quoted=r'("([^"\\]|(\\.))*")'))
+ m = r.match(line)
+ if m:
+ d = m.groupdict()
+ return (d["before"], d["after"])
+ return (line, "")
def _simplifyargs(argsline):
a = []
@@ -642,12 +652,17 @@ def crackline(line, reset=0):
global filepositiontext, currentfilename, neededmodule, expectbegin
global skipblocksuntil, skipemptyends, previous_context, gotnextfile
- if ';' in line and not (f2pyenhancementspattern[0].match(line) or
- multilinepattern[0].match(line)):
- for l in line.split(';'):
- # XXX: non-zero reset values need testing
- assert reset == 0, repr(reset)
- crackline(l, reset)
+ _, has_semicolon = split_by_unquoted(line, ";")
+ if has_semicolon and not (f2pyenhancementspattern[0].match(line) or
+ multilinepattern[0].match(line)):
+ # XXX: non-zero reset values need testing
+ assert reset == 0, repr(reset)
+ # split line on unquoted semicolons
+ line, semicolon_line = split_by_unquoted(line, ";")
+ while semicolon_line:
+ crackline(line, reset)
+ line, semicolon_line = split_by_unquoted(semicolon_line[1:], ";")
+ crackline(line, reset)
return
if reset < 0:
groupcounter = 0
@@ -802,26 +817,22 @@ def markouterparen(line):
def markoutercomma(line, comma=','):
l = ''
f = 0
- cc = ''
- for c in line:
- if (not cc or cc == ')') and c == '(':
- f = f + 1
- cc = ')'
- elif not cc and c == '\'' and (not l or l[-1] != '\\'):
- f = f + 1
- cc = '\''
- elif c == cc:
- f = f - 1
- if f == 0:
- cc = ''
- elif c == comma and f == 0:
- l = l + '@' + comma + '@'
- continue
- l = l + c
- assert not f, repr((f, line, l, cc))
+ before, after = split_by_unquoted(line, comma + '()')
+ l += before
+ while after:
+ if (after[0] == comma) and (f == 0):
+ l += '@' + comma + '@'
+ else:
+ l += after[0]
+ if after[0] == '(':
+ f += 1
+ elif after[0] == ')':
+ f -= 1
+ before, after = split_by_unquoted(after[1:], comma + '()')
+ l += before
+ assert not f, repr((f, line, l))
return l
-
def unmarkouterparen(line):
r = line.replace('@(@', '(').replace('@)@', ')')
return r
diff --git a/numpy/f2py/src/fortranobject.c b/numpy/f2py/src/fortranobject.c
index a47733433..dd2484eb4 100644
--- a/numpy/f2py/src/fortranobject.c
+++ b/numpy/f2py/src/fortranobject.c
@@ -241,7 +241,7 @@ fortran_doc(FortranDataDef def)
static FortranDataDef *save_def; /* save pointer of an allocatable array */
static void set_data(char *d,npy_intp *f) { /* callback from Fortran */
- if (*f) /* In fortran f=allocated(d) */
+ if (*f) /* In fortran f=allocated(d) */
save_def->data = d;
else
save_def->data = NULL;
@@ -439,23 +439,23 @@ PyTypeObject PyFortran_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
#else
PyObject_HEAD_INIT(0)
- 0, /*ob_size*/
+ 0, /*ob_size*/
#endif
"fortran", /*tp_name*/
sizeof(PyFortranObject), /*tp_basicsize*/
- 0, /*tp_itemsize*/
+ 0, /*tp_itemsize*/
/* methods */
- (destructor)fortran_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
+ (destructor)fortran_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
(getattrfunc)fortran_getattr, /*tp_getattr*/
(setattrfunc)fortran_setattr, /*tp_setattr*/
- 0, /*tp_compare/tp_reserved*/
- (reprfunc)fortran_repr, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash*/
- (ternaryfunc)fortran_call, /*tp_call*/
+ 0, /*tp_compare/tp_reserved*/
+ (reprfunc)fortran_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ (ternaryfunc)fortran_call, /*tp_call*/
};
/************************* f2py_report_atexit *******************************/
@@ -656,17 +656,18 @@ PyArrayObject* array_from_pyobj(const int type_num,
const int rank,
const int intent,
PyObject *obj) {
- /* Note about reference counting
- -----------------------------
- If the caller returns the array to Python, it must be done with
- Py_BuildValue("N",arr).
- Otherwise, if obj!=arr then the caller must call Py_DECREF(arr).
-
- Note on intent(cache,out,..)
- ---------------------
- Don't expect correct data when returning intent(cache) array.
-
- */
+ /*
+ * Note about reference counting
+ * -----------------------------
+ * If the caller returns the array to Python, it must be done with
+ * Py_BuildValue("N",arr).
+ * Otherwise, if obj!=arr then the caller must call Py_DECREF(arr).
+ *
+ * Note on intent(cache,out,..)
+ * ---------------------
+ * Don't expect correct data when returning intent(cache) array.
+ *
+ */
char mess[200];
PyArrayObject *arr = NULL;
PyArray_Descr *descr;
@@ -744,17 +745,17 @@ PyArrayObject* array_from_pyobj(const int type_num,
if (check_and_fix_dimensions(arr, rank, dims)) {
return NULL;
}
- /*
- printf("intent alignment=%d\n", F2PY_GET_ALIGNMENT(intent));
- printf("alignment check=%d\n", F2PY_CHECK_ALIGNMENT(arr, intent));
- int i;
- for (i=1;i<=16;i++)
- printf("i=%d isaligned=%d\n", i, ARRAY_ISALIGNED(arr, i));
- */
+ /*
+ printf("intent alignment=%d\n", F2PY_GET_ALIGNMENT(intent));
+ printf("alignment check=%d\n", F2PY_CHECK_ALIGNMENT(arr, intent));
+ int i;
+ for (i=1;i<=16;i++)
+ printf("i=%d isaligned=%d\n", i, ARRAY_ISALIGNED(arr, i));
+ */
if ((! (intent & F2PY_INTENT_COPY))
&& PyArray_ITEMSIZE(arr)==elsize
&& ARRAY_ISCOMPATIBLE(arr,type_num)
- && F2PY_CHECK_ALIGNMENT(arr, intent)
+ && F2PY_CHECK_ALIGNMENT(arr, intent)
) {
if ((intent & F2PY_INTENT_C)?PyArray_ISCARRAY(arr):PyArray_ISFARRAY(arr)) {
if ((intent & F2PY_INTENT_OUT)) {
@@ -780,8 +781,8 @@ PyArrayObject* array_from_pyobj(const int type_num,
if (!(ARRAY_ISCOMPATIBLE(arr,type_num)))
sprintf(mess+strlen(mess)," -- input '%c' not compatible to '%c'",
PyArray_DESCR(arr)->type,typechar);
- if (!(F2PY_CHECK_ALIGNMENT(arr, intent)))
- sprintf(mess+strlen(mess)," -- input not %d-aligned", F2PY_GET_ALIGNMENT(intent));
+ if (!(F2PY_CHECK_ALIGNMENT(arr, intent)))
+ sprintf(mess+strlen(mess)," -- input not %d-aligned", F2PY_GET_ALIGNMENT(intent));
PyErr_SetString(PyExc_ValueError,mess);
return NULL;
}
@@ -858,14 +859,14 @@ static
int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp *dims)
{
/*
- This function fills in blanks (that are -1's) in dims list using
- the dimensions from arr. It also checks that non-blank dims will
- match with the corresponding values in arr dimensions.
-
- Returns 0 if the function is successful.
-
- If an error condition is detected, an exception is set and 1 is returned.
- */
+ * This function fills in blanks (that are -1's) in dims list using
+ * the dimensions from arr. It also checks that non-blank dims will
+ * match with the corresponding values in arr dimensions.
+ *
+ * Returns 0 if the function is successful.
+ *
+ * If an error condition is detected, an exception is set and 1 is returned.
+ */
const npy_intp arr_size = (PyArray_NDIM(arr))?PyArray_Size((PyObject *)arr):1;
#ifdef DEBUG_COPY_ND_ARRAY
dump_attrs(arr);
@@ -922,7 +923,7 @@ int check_and_fix_dimensions(const PyArrayObject* arr, const int rank, npy_intp
int i;
npy_intp d;
for (i=0; i<rank; ++i) {
- d = PyArray_DIM(arr,i);
+ d = PyArray_DIM(arr,i);
if (dims[i]>=0) {
if (d > 1 && d!=dims[i]) {
PyErr_Format(PyExc_ValueError,
diff --git a/numpy/f2py/src/fortranobject.h b/numpy/f2py/src/fortranobject.h
index c9b54e259..5d0dcf676 100644
--- a/numpy/f2py/src/fortranobject.h
+++ b/numpy/f2py/src/fortranobject.h
@@ -78,16 +78,16 @@ typedef void *(*f2pycfunc)(void);
typedef struct {
char *name; /* attribute (array||routine) name */
int rank; /* array rank, 0 for scalar, max is F2PY_MAX_DIMS,
- || rank=-1 for Fortran routine */
+ || rank=-1 for Fortran routine */
struct {npy_intp d[F2PY_MAX_DIMS];} dims; /* dimensions of the array, || not used */
int type; /* PyArray_<type> || not used */
char *data; /* pointer to array || Fortran routine */
- f2py_init_func func; /* initialization function for
- allocatable arrays:
- func(&rank,dims,set_ptr_func,name,len(name))
- || C/API wrapper for Fortran routine */
+ f2py_init_func func; /* initialization function for
+ allocatable arrays:
+ func(&rank,dims,set_ptr_func,name,len(name))
+ || C/API wrapper for Fortran routine */
char *doc; /* documentation string; only recommended
- for routines. */
+ for routines. */
} FortranDataDef;
typedef struct {
@@ -139,16 +139,16 @@ int F2PyCapsule_Check(PyObject *ptr);
#define F2PY_ALIGN16(intent) (intent & F2PY_INTENT_ALIGNED16)
#define F2PY_GET_ALIGNMENT(intent) \
- (F2PY_ALIGN4(intent) ? 4 : \
- (F2PY_ALIGN8(intent) ? 8 : \
- (F2PY_ALIGN16(intent) ? 16 : 1) ))
+ (F2PY_ALIGN4(intent) ? 4 : \
+ (F2PY_ALIGN8(intent) ? 8 : \
+ (F2PY_ALIGN16(intent) ? 16 : 1) ))
#define F2PY_CHECK_ALIGNMENT(arr, intent) ARRAY_ISALIGNED(arr, F2PY_GET_ALIGNMENT(intent))
extern PyArrayObject* array_from_pyobj(const int type_num,
- npy_intp *dims,
- const int rank,
- const int intent,
- PyObject *obj);
+ npy_intp *dims,
+ const int rank,
+ const int intent,
+ PyObject *obj);
extern int copy_ND_array(const PyArrayObject *in, PyArrayObject *out);
#ifdef DEBUG_COPY_ND_ARRAY
diff --git a/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c b/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
index 22801abdc..7f46303b0 100644
--- a/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
+++ b/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
@@ -33,7 +33,7 @@ Required arguments:\n"
"Return objects:\n"
" arr : array";
static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
- PyObject *capi_args) {
+ PyObject *capi_args) {
PyObject * volatile capi_buildvalue = NULL;
int type_num = 0;
npy_intp *dims = NULL;
@@ -45,7 +45,7 @@ static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
int i;
if (!PyArg_ParseTuple(capi_args,"iOiO|:wrap.call",\
- &type_num,&dims_capi,&intent,&arr_capi))
+ &type_num,&dims_capi,&intent,&arr_capi))
return NULL;
rank = PySequence_Length(dims_capi);
dims = malloc(rank*sizeof(npy_intp));
@@ -78,7 +78,7 @@ Required arguments:\n"
" itemsize : int\n"
;
static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self,
- PyObject *capi_args) {
+ PyObject *capi_args) {
PyObject *arr_capi = Py_None;
PyArrayObject *arr = NULL;
PyObject *dimensions = NULL;
@@ -87,7 +87,7 @@ static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self,
int i;
memset(s,0,100*sizeof(char));
if (!PyArg_ParseTuple(capi_args,"O!|:wrap.attrs",
- &PyArray_Type,&arr_capi))
+ &PyArray_Type,&arr_capi))
return NULL;
arr = (PyArrayObject *)arr_capi;
sprintf(s,"%p",PyArray_DATA(arr));
@@ -98,15 +98,15 @@ static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self,
PyTuple_SetItem(strides,i,PyInt_FromLong(PyArray_STRIDE(arr,i)));
}
return Py_BuildValue("siOOO(cciii)ii",s,PyArray_NDIM(arr),
- dimensions,strides,
- (PyArray_BASE(arr)==NULL?Py_None:PyArray_BASE(arr)),
- PyArray_DESCR(arr)->kind,
- PyArray_DESCR(arr)->type,
- PyArray_TYPE(arr),
- PyArray_ITEMSIZE(arr),
- PyArray_DESCR(arr)->alignment,
- PyArray_FLAGS(arr),
- PyArray_ITEMSIZE(arr));
+ dimensions,strides,
+ (PyArray_BASE(arr)==NULL?Py_None:PyArray_BASE(arr)),
+ PyArray_DESCR(arr)->kind,
+ PyArray_DESCR(arr)->type,
+ PyArray_TYPE(arr),
+ PyArray_ITEMSIZE(arr),
+ PyArray_DESCR(arr)->alignment,
+ PyArray_FLAGS(arr),
+ PyArray_ITEMSIZE(arr));
}
static PyMethodDef f2py_module_methods[] = {
diff --git a/numpy/f2py/tests/test_quoted_character.py b/numpy/f2py/tests/test_quoted_character.py
new file mode 100644
index 000000000..4770c11c4
--- /dev/null
+++ b/numpy/f2py/tests/test_quoted_character.py
@@ -0,0 +1,32 @@
+from __future__ import division, absolute_import, print_function
+
+from . import util
+
+from numpy.testing import run_module_suite, assert_equal, dec
+
+import sys
+
+class TestQuotedCharacter(util.F2PyTest):
+ code = """
+ SUBROUTINE FOO(OUT1, OUT2, OUT3, OUT4, OUT5, OUT6)
+ CHARACTER SINGLE, DOUBLE, SEMICOL, EXCLA, OPENPAR, CLOSEPAR
+ PARAMETER (SINGLE="'", DOUBLE='"', SEMICOL=';', EXCLA="!",
+ 1 OPENPAR="(", CLOSEPAR=")")
+ CHARACTER OUT1, OUT2, OUT3, OUT4, OUT5, OUT6
+Cf2py intent(out) OUT1, OUT2, OUT3, OUT4, OUT5, OUT6
+ OUT1 = SINGLE
+ OUT2 = DOUBLE
+ OUT3 = SEMICOL
+ OUT4 = EXCLA
+ OUT5 = OPENPAR
+ OUT6 = CLOSEPAR
+ RETURN
+ END
+ """
+
+ @dec.knownfailureif(sys.platform=='win32', msg='Fails with MinGW64 Gfortran (Issue #9673)')
+ def test_quoted_character(self):
+ assert_equal(self.module.foo(), (b"'", b'"', b';', b'!', b'(', b')'))
+
+if __name__ == "__main__":
+ run_module_suite()
diff --git a/numpy/f2py/tests/test_semicolon_split.py b/numpy/f2py/tests/test_semicolon_split.py
new file mode 100644
index 000000000..2b0f32727
--- /dev/null
+++ b/numpy/f2py/tests/test_semicolon_split.py
@@ -0,0 +1,53 @@
+from __future__ import division, absolute_import, print_function
+
+from . import util
+from numpy.testing import assert_equal
+
+class TestMultiline(util.F2PyTest):
+ suffix = ".pyf"
+ module_name = "multiline"
+ code = """
+python module {module}
+ usercode '''
+void foo(int* x) {{
+ char dummy = ';';
+ *x = 42;
+}}
+'''
+ interface
+ subroutine foo(x)
+ intent(c) foo
+ integer intent(out) :: x
+ end subroutine foo
+ end interface
+end python module {module}
+ """.format(module=module_name)
+
+ def test_multiline(self):
+ assert_equal(self.module.foo(), 42)
+
+class TestCallstatement(util.F2PyTest):
+ suffix = ".pyf"
+ module_name = "callstatement"
+ code = """
+python module {module}
+ usercode '''
+void foo(int* x) {{
+}}
+'''
+ interface
+ subroutine foo(x)
+ intent(c) foo
+ integer intent(out) :: x
+ callprotoargument int*
+ callstatement {{ &
+ ; &
+ x = 42; &
+ }}
+ end subroutine foo
+ end interface
+end python module {module}
+ """.format(module=module_name)
+
+ def test_callstatement(self):
+ assert_equal(self.module.foo(), 42)