Skip to content

Commit 86f568b

Browse files
committed
Support functions returning results as an array
Fixes #13
1 parent 53ed51e commit 86f568b

File tree

5 files changed

+52
-19
lines changed

5 files changed

+52
-19
lines changed

gfort2py/fArrays.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ def print(self):
223223
print(f"\t ubound {self.cvalue.dims[i].ubound}")
224224
print(f"\t stride {self.cvalue.dims[i].stride}")
225225

226+
def _make_empty(self):
227+
dtype = self.obj.dtype()
228+
shape = self.obj.shape()
229+
230+
return np.zeros(shape, dtype=dtype, order="F")
231+
226232

227233
class fAssumedSize(fArray_t):
228234
def ctype(self):

gfort2py/fProc.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from .fVar import fVar
1010
from .fVar_t import fVar_t
11+
from .utils import resolve_other_args
1112

1213
_TEST_FLAG = os.environ.get("_GFORT2PY_TEST_FLAG") is not None
1314

@@ -72,8 +73,6 @@ def name(self):
7273
return self.obj.name
7374

7475
def __call__(self, *args, **kwargs):
75-
self._set_return()
76-
7776
func_args = self._convert_args(*args, **kwargs)
7877

7978
with _captureStdOut() as cs:
@@ -84,13 +83,13 @@ def __call__(self, *args, **kwargs):
8483

8584
return self._convert_result(res, func_args)
8685

87-
def _set_return(self):
86+
def _set_return(self, other_args):
8887
if self.obj.is_subroutine():
8988
self._func.restype = None # Subroutine
9089
else:
91-
if (
92-
self.return_var.obj.is_char()
93-
): # Returning a character is done as a character + len at start of arg list
90+
self.return_var.obj = resolve_other_args(self.return_var.obj, other_args)
91+
92+
if self.return_var.obj.is_returned_as_arg():
9493
self._func.restype = None
9594
else:
9695
self._func.restype = self.return_var.ctype()
@@ -102,6 +101,9 @@ def args_start(self):
102101
l = self.return_var.len()
103102
res.append(self.return_var.from_param(" " * l))
104103
res.append(self.return_var.ctype_len())
104+
elif self.return_var.obj.is_always_explicit() and self.obj.is_array():
105+
empty_array = self.return_var._make_empty()
106+
res.append(ctypes.pointer(self.return_var.from_param(empty_array)))
105107

106108
return res
107109

@@ -145,10 +147,13 @@ def args_convert(self, input_args):
145147
return args, args_end
146148

147149
def _convert_args(self, *args, **kwargs):
148-
args_start = self.args_start()
149-
150150
self.input_args = self.args_check(*args, **kwargs)
151151

152+
# Set this now after arsg_check as we need to be able to
153+
# resolve runtime arguments
154+
self._set_return(self.input_args)
155+
args_start = self.args_start()
156+
152157
args_mid, args_end = self.args_convert(self.input_args)
153158

154159
return args_start + args_mid + args_end
@@ -157,10 +162,14 @@ def _convert_result(self, result, args):
157162
res = {}
158163

159164
if self.obj.is_function():
160-
if self.return_var.obj.is_char():
161-
result = args[0]
162-
_ = args.pop(0)
163-
_ = args.pop(0) # Twice to pop first and second value
165+
if self.return_var.obj.is_returned_as_arg():
166+
if self.return_var.obj.is_char():
167+
result = args[0]
168+
_ = args.pop(0)
169+
_ = args.pop(0) # Twice to pop first and second value
170+
elif self.return_var.obj.is_always_explicit() and self.obj.is_array():
171+
result = self.return_var.value
172+
_ = args.pop(0)
164173

165174
if len(self.obj.args()):
166175
for var in self.input_args:

gfort2py/fVar.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ def __new__(cls, obj, *args, **kwargs):
3737
return fExplicitArr(obj, *args, **kwargs)
3838
elif obj.is_assumed_size():
3939
return fAssumedSize(obj, *args, **kwargs)
40-
elif obj.is_assumed_shape() or obj.is_allocatable() or obj.is_pointer():
40+
elif (
41+
obj.is_assumed_shape()
42+
or obj.is_allocatable()
43+
or obj.is_pointer()
44+
or obj.is_always_explicit()
45+
):
4146
return fAssumedShape(obj, *args, **kwargs)
4247
else:
4348
raise TypeError("Unknown array type")

gfort2py/module_parse.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,6 @@ def is_function(self):
254254
def is_array(self):
255255
return "DIMENSION" in self.sym.attr.attributes
256256

257-
def is_always_explicit(self):
258-
return "ALWAYS_EXPLICIT" in self.sym.attr.attributes
259-
260257
def is_dummy(self):
261258
return "DUMMY" in self.sym.attr.attributes
262259

@@ -269,6 +266,9 @@ def is_return(self):
269266
def needs_array_desc(self):
270267
return self.is_dummy() or self.is_allocatable() or self.is_always_explicit()
271268

269+
def is_always_explicit(self):
270+
return "ALWAYS_EXPLICIT" in self.sym.attr.attributes
271+
272272
def not_a_pointer(self):
273273
return (
274274
self.needs_array_desc()
@@ -289,6 +289,11 @@ def is_assumed_size(self):
289289
def is_assumed_shape(self):
290290
return self.sym.array_spec.array_type == "ASSUMED_SHAPE"
291291

292+
def is_returned_as_arg(self):
293+
return (self.is_always_explicit() and self.is_return() and self.is_array()) or (
294+
self.is_char()
295+
)
296+
292297
def is_deferred_len(self):
293298
# Only needed for things that need an extra function argument for their length
294299
if self.is_char():

tests/explicit_arrays_test.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,11 @@ def test_logical_arr_multi(self):
256256
y = x.func_logical_multi(1.0, 2.0, xarr, 3.0, 4.0)
257257
self.assertEqual(y.result, True)
258258

259-
@pytest.mark.skip
260259
def test_mesh_exp(self):
261260
# Github issue #13
262261
i = 5
263262
y = x.func_mesh_exp(i)
264-
np.array_equal(y.result, np.arrange(0, i + 1 + 1))
263+
np.array_equal(y.result, np.arange(0, i + 1 + 1))
265264

266265
def test_mesh_exp2(self):
267266
i = 5
@@ -303,7 +302,16 @@ def test_logical_param_array(self):
303302
x.const_logical_arr, np.array([True, False, True, False, True], dtype=bool)
304303
)
305304

306-
@pytest.mark.skip
307305
def test_func_return_1d_int_arr(self):
308306
res = x.func_return_1d_int_arr()
309307
assert np.array_equal(res.result, np.array([1, 2, 3, 4, 5]))
308+
309+
def test_func_return_1d_int_arr_n(self):
310+
res = x.func_return_1d_int_arr_n(5)
311+
assert np.array_equal(res.result, np.array([1, 2, 3, 4, 5]))
312+
313+
def test_func_return_2d_int_arr(self):
314+
res = x.func_return_2d_int_arr()
315+
assert np.array_equal(
316+
res.result, np.array([1, 2, 3, 4, 5, 6]).reshape(3, 2, order="F")
317+
)

0 commit comments

Comments
 (0)