@@ -43,6 +43,11 @@ from .paridecl cimport *
4343from .stack cimport new_gen, clone_gen_noclear, DetachGen
4444from .gen cimport objtogen
4545
46+ try :
47+ from inspect import getfullargspec as getargspec
48+ except ImportError :
49+ from inspect import getargspec
50+
4651
4752cdef 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.
120126cdef 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
126135cpdef 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
0 commit comments