Skip to content

Commit 4721314

Browse files
committed
Support closures of specific arity
1 parent f799b9d commit 4721314

File tree

3 files changed

+61
-23
lines changed

3 files changed

+61
-23
lines changed

cypari2/closure.pxd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .gen cimport Gen
22
cpdef Gen objtoclosure(f)
3-
cdef void _pari_init_closure()
3+
cdef int _pari_init_closure() except -1

cypari2/closure.pyx

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ from .paridecl cimport *
4343
from .stack cimport new_gen, clone_gen_noclear, DetachGen
4444
from .gen cimport objtogen
4545

46+
try:
47+
from inspect import getfullargspec as getargspec
48+
except ImportError:
49+
from inspect import getargspec
50+
4651

4752
cdef inline GEN call_python_func_impl "call_python_func"(GEN* args, object py_func) except NULL:
4853
"""
@@ -84,26 +89,26 @@ cdef extern from *:
8489
GEN call_python_func(GEN* args, PyObject* py_func)
8590

8691

87-
cdef GEN call_python(GEN arg1, GEN arg2, GEN arg3, GEN arg4, GEN arg5, ulong py_func):
92+
cdef GEN call_python(GEN arg1, GEN arg2, GEN arg3, GEN arg4, GEN arg5, ulong nargs, ulong py_func):
8893
"""
8994
This function, which will be installed in PARI, is a front-end for
9095
``call_python_func_impl``.
9196
92-
It has 5 optional ``GEN``s as argument and one ``ulong``.
93-
This last argument is actually a Python callable object cast to
94-
``ulong``.
97+
It has 5 optional ``GEN``s as argument, a ``nargs`` argument
98+
specifying how many arguments are valid and one ``ulong``, which is
99+
actually a Python callable object cast to ``ulong``.
95100
"""
96-
# Convert arguments to a NULL-terminated array. From PARI's point
97-
# of view, all these arguments are optional: if an argument is not
98-
# given, PARI will pass NULL as argument and the array will
99-
# terminate sooner.
101+
if nargs > 5:
102+
sig_error()
103+
104+
# Convert arguments to a NULL-terminated array.
100105
cdef GEN args[6]
101106
args[0] = arg1
102107
args[1] = arg2
103108
args[2] = arg3
104109
args[3] = arg4
105110
args[4] = arg5
106-
args[5] = NULL
111+
args[nargs] = NULL
107112

108113
sig_block()
109114
# Disallow interrupts during the Python code inside
@@ -116,12 +121,16 @@ cdef GEN call_python(GEN arg1, GEN arg2, GEN arg3, GEN arg4, GEN arg5, ulong py_
116121
sig_error()
117122
return r
118123

124+
119125
# Install the function "call_python" for use in the PARI library.
120126
cdef entree* ep_call_python
121127

122-
cdef void _pari_init_closure():
128+
cdef int _pari_init_closure() except -1:
129+
sig_on()
123130
global ep_call_python
124-
ep_call_python = install(<void*>call_python, "call_python", "DGDGDGDGDGU")
131+
ep_call_python = install(<void*>call_python, "call_python", 'DGDGDGDGDGD5,U,U')
132+
sig_off()
133+
125134

126135
cpdef Gen objtoclosure(f):
127136
"""
@@ -145,11 +154,13 @@ cpdef Gen objtoclosure(f):
145154
>>> def pymul(i,j): return i*j
146155
>>> mul = objtoclosure(pymul)
147156
>>> mul
148-
(v1,v2,v3,v4,v5)->call_python(v1,v2,v3,v4,v5,...)
149-
>>> mul.type()
150-
't_CLOSURE'
157+
(v1,v2)->call_python(v1,v2,0,0,0,2,...)
151158
>>> mul(6,9)
152159
54
160+
>>> mul.type()
161+
't_CLOSURE'
162+
>>> mul.arity()
163+
2
153164
>>> def printme(x):
154165
... print(x)
155166
>>> objtoclosure(printme)('matid(2)')
@@ -174,10 +185,37 @@ cpdef Gen objtoclosure(f):
174185
...
175186
PariError: call_python: forbidden multiplication t_VEC (1 elts) * t_VEC (1 elts)
176187
"""
188+
if not callable(f):
189+
raise TypeError("argument to objtoclosure() must be callable")
190+
191+
# Determine number of arguments of f
192+
cdef Py_ssize_t i, nargs
193+
try:
194+
argspec = getargspec(f)
195+
except Exception:
196+
nargs = 5
197+
else:
198+
nargs = len(argspec.args)
199+
200+
# Only 5 arguments are supported for now...
201+
if nargs > 5:
202+
nargs = 5
203+
204+
# Fill in default arguments of PARI function
177205
sig_on()
206+
cdef GEN args = cgetg((5 - nargs) + 2 + 1, t_VEC)
207+
for i in range(5 - nargs):
208+
set_gel(args, i + 1, gnil)
209+
set_gel(args, (5 - nargs) + 1, stoi(nargs))
178210
# Convert f to a t_INT containing the address of f
179-
cdef GEN f_int = utoi(<ulong><PyObject*>f)
211+
set_gel(args, (5 - nargs) + 1 + 1, utoi(<ulong><PyObject*>f))
212+
180213
# Create a t_CLOSURE which calls call_python() with py_func equal to f
181-
cdef Gen c = new_gen(snm_closure(ep_call_python, mkvec(f_int)))
182-
Py_INCREF(f) # we need to keep a reference to f somewhere...
183-
return c
214+
cdef Gen res = new_gen(snm_closure(ep_call_python, args))
215+
216+
# We need to keep a reference to f somewhere and there is no way to
217+
# have PARI handle this reference for us. So the only way out is to
218+
# force f to be never deallocated
219+
Py_INCREF(f)
220+
221+
return res

cypari2/cypari.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
/* Array element assignment */
10-
#define set_gel(x, n, z) (gel(x,n) = z)
11-
#define set_gmael(x, i, j, z) (gmael(x,i,j) = z)
12-
#define set_gcoeff(x, i, j, z) (gcoeff(x,i,j) = z)
13-
#define set_uel(x, n, z) (uel(x,n) = z)
10+
#define set_gel(x, n, z) (gel((x), (n)) = (z))
11+
#define set_gmael(x, i, j, z) (gmael((x), (i), (j)) = (z))
12+
#define set_gcoeff(x, i, j, z) (gcoeff((x), (i), (j)) = (z))
13+
#define set_uel(x, n, z) (uel((x), (n)) = (z))

0 commit comments

Comments
 (0)