Skip to content

Commit bc7c70c

Browse files
committed
Make all GP functions available as methods of Pari class
1 parent 16159d4 commit bc7c70c

File tree

4 files changed

+58
-15
lines changed

4 files changed

+58
-15
lines changed

autogen/args.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ def convert_code(self):
191191
elif self.default is None:
192192
s = " {name} = objtogen({name})\n"
193193
s += " cdef GEN {tmp} = (<Gen>{name}).g\n"
194+
elif self.default is False:
195+
# This is actually a required argument
196+
# See parse_prototype() in parser.py why we need this
197+
s = " if {name} is None:\n"
198+
s += " raise TypeError(\"missing required argument: '{name}'\")\n"
199+
s += " {name} = objtogen({name})\n"
200+
s += " cdef GEN {tmp} = (<Gen>{name}).g\n"
194201
elif self.default == "NULL":
195202
s = " cdef GEN {tmp} = {default}\n"
196203
s += " if {name} is not None:\n"

autogen/generator.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -190,20 +190,19 @@ def bernvec(self, long x):
190190
except NotImplementedError:
191191
return # Skip unsupported prototype codes
192192

193-
# Is the first argument a GEN?
193+
doc = get_rest_doc(function)
194+
194195
if len(args) > 0 and isinstance(args[0], PariArgumentGEN):
195-
# If yes, write a method of the Gen class.
196-
cargs = args
197-
f = self.gen_file
198-
else:
199-
# If no, write a method of the Pari class.
200-
# Parse again with an extra "self" argument.
201-
args, ret = parse_prototype(prototype, help, [PariInstanceArgument()])
202-
cargs = args[1:]
203-
f = self.instance_file
204-
205-
self.write_method(function, cname, args, ret, cargs, f,
206-
get_rest_doc(function), obsolete)
196+
# If the first argument is a GEN, write a method of the
197+
# Gen class.
198+
self.write_method(function, cname, args, ret, args,
199+
self.gen_file, doc, obsolete)
200+
201+
# In any case, write a method of the Pari class.
202+
# Parse again with an extra "self" argument.
203+
args, ret = parse_prototype(prototype, help, [PariInstanceArgument()])
204+
self.write_method(function, cname, args, ret, args[1:],
205+
self.instance_file, doc, obsolete)
207206

208207
def write_method(self, function, cname, args, ret, cargs, file, doc, obsolete):
209208
"""

autogen/parser.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def parse_prototype(proto, help, initial_args=[]):
156156

157157
# Go over the prototype characters and build up the arguments
158158
args = list(initial_args)
159-
default = None
159+
have_default = False # Have we seen any default argument?
160160
while n < len(proto):
161161
c = proto[n]; n += 1
162162

@@ -184,6 +184,25 @@ def parse_prototype(proto, help, initial_args=[]):
184184
raise ValueError('unknown prototype character %r' % c)
185185

186186
arg = t(names, default, index=len(args))
187+
if arg.default is not None:
188+
have_default = True
189+
elif have_default:
190+
# We have a non-default argument following a default
191+
# argument, which means trouble...
192+
#
193+
# A syntactical wart of Python is that it does not allow
194+
# that: something like def foo(x=None, y) is a SyntaxError
195+
# (at least with Python-2.7.13, Python-3.6.1 and Cython-0.25.2)
196+
#
197+
# A small number of GP functions (nfroots() for example)
198+
# wants to do this anyway. Luckily, this seems to occur only
199+
# for arguments of type GEN (prototype code "G")
200+
#
201+
# To work around this, we add a "fake" default value and
202+
# then raise an error if it was not given...
203+
if c != "G":
204+
raise NotImplementedError("non-default argument after default argument is only implemented for GEN arguments")
205+
arg.default = False
187206
args.append(arg)
188207

189208
return (args, ret)

cypari2/pari_instance.pyx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,24 @@ Examples:
4141
(120*x + 10)/x
4242
>>> pari('intnum(x=0,13,sin(x)+sin(x^2) + x)')
4343
85.6215190762676
44-
>>> f = pari('x^3-1')
44+
>>> f = pari('x^3 - 1')
4545
>>> v = f.factor(); v
4646
[x - 1, 1; x^2 + x + 1, 1]
4747
>>> v[0] # indexing is 0-based unlike in GP.
4848
[x - 1, x^2 + x + 1]~
4949
>>> v[1]
5050
[1, 1]~
5151
52+
For most functions, you can call the function as method of ``pari``
53+
or you can first create a :class:`Gen` object and then call the
54+
function as method of that. In other words, the following two commands
55+
do the same:
56+
57+
>>> pari('x^3 - 1').factor()
58+
[x - 1, 1; x^2 + x + 1, 1]
59+
>>> pari.factor('x^3 - 1')
60+
[x - 1, 1; x^2 + x + 1, 1]
61+
5262
Arithmetic operations cause all arguments to be converted to PARI:
5363
5464
>>> type(pari(1) + 1)
@@ -221,6 +231,14 @@ Python (:trac:`9636`):
221231
>>> pari('print("test")')
222232
test
223233
234+
Verify that ``nfroots()`` (which has an unusual signature with a
235+
non-default argument following a default argument) works:
236+
237+
>>> pari.nfroots(x='x^4 - 1')
238+
[-1, 1]
239+
>>> pari.nfroots(pari.nfinit('t^2 + 1'), "x^4 - 1")
240+
[-1, 1, Mod(-t, t^2 + 1), Mod(t, t^2 + 1)]
241+
224242
"""
225243

226244
#*****************************************************************************

0 commit comments

Comments
 (0)