File: args.py

package info (click to toggle)
cypari2 2.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 908 kB
  • sloc: python: 972; makefile: 34; ansic: 5
file content (363 lines) | stat: -rw-r--r-- 11,803 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
"""
Arguments for PARI calls
"""

#*****************************************************************************
#       Copyright (C) 2015 Jeroen Demeyer <jdemeyer@cage.ugent.be>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#                  http://www.gnu.org/licenses/
#*****************************************************************************

from __future__ import unicode_literals

# Some replacements for reserved words
replacements = {'char': 'character'}

class PariArgument(object):
    """
    This class represents one argument in a PARI call.
    """
    def __init__(self, namesiter, default, index):
        """
        Create a new argument for a PARI call.

        INPUT:

        - ``namesiter`` -- iterator over all names of the arguments.
          Usually, the next name from this iterator is used as argument
          name.

        - ``default`` -- default value for this argument (``None``
          means that the argument is not optional).

        - ``index`` -- (integer >= 0). Index of this argument in the
          list of arguments. Index 0 means a ``"self"`` argument which
          is treated specially. For a function which is not a method,
          start counting at 1.
        """
        self.index = index
        try:
            self.name = self.get_argument_name(namesiter)
        except StopIteration:
            # No more names available, use something default.
            # This is used in listcreate() and polsturm() for example
            # which have deprecated arguments which are not listed in
            # the help.
            self.name = "_arg%s" % index
            self.undocumented = True
        else:
            self.undocumented = False

        if self.index == 0:  # "self" argument can never have a default
            self.default = None
        elif default is None:
            self.default = self.always_default()
        elif default == "":
            self.default = self.default_default()
        else:
            self.default = default

        # Name for a temporary variable. Only a few classes actually use this.
        self.tmpname = "_" + self.name

    def __repr__(self):
        s = self._typerepr() + " " + self.name
        if self.default is not None:
            s += "=" + self.default
        return s

    def _typerepr(self):
        """
        Return a string representing the type of this argument.
        """
        return "(generic)"

    def ctype(self):
        """
        The corresponding C type. This is used for auto-generating
        the declarations of the C function. In some cases, this is also
        used for passing the argument from Python to Cython.
        """
        raise NotImplementedError

    def always_default(self):
        """
        If this returns not ``None``, it is a value which is always
        the default for this argument, which is then automatically
        optional.
        """
        return None

    def default_default(self):
        """
        The default value for an optional argument if no other default
        was specified in the prototype.
        """
        return "NULL"

    def get_argument_name(self, namesiter):
        """
        Return the name for this argument, given ``namesiter`` which is
        an iterator over the argument names given by the help string.
        """
        n = next(namesiter)
        try:
            return replacements[n]
        except KeyError:
            return n

    def prototype_code(self):
        """
        Return code to appear in the prototype of the Cython wrapper.
        """
        raise NotImplementedError

    def deprecation_warning_code(self, function):
        """
        Return code to appear in the function body to give a
        deprecation warning for this argument, if applicable.
        ``function`` is the function name to appear in the message.
        """
        if not self.undocumented:
            return ""
        s  = "        if {name} is not None:\n"
        s += "            from warnings import warn\n"
        s += "            warn('argument {index} of the PARI/GP function {function} is undocumented and deprecated', DeprecationWarning)\n"
        return s.format(name=self.name, index=self.index, function=function)

    def convert_code(self):
        """
        Return code to appear in the function body to convert this
        argument to something that PARI understand. This code can also
        contain extra checks. It will run outside of ``sig_on()``.
        """
        return ""

    def c_convert_code(self):
        """
        Return additional conversion code which will be run after
        ``convert_code`` and inside the ``sig_on()`` block. This must
        not involve any Python code (in particular, it should not raise
        exceptions).
        """
        return ""

    def call_code(self):
        """
        Return code to put this argument in a PARI function call.
        """
        return self.name


class PariArgumentObject(PariArgument):
    """
    Class for arguments which are passed as generic Python ``object``.
    """
    def prototype_code(self):
        """
        Return code to appear in the prototype of the Cython wrapper.
        """
        s = self.name
        if self.default is not None:
            # Default corresponds to None, actual default value should
            # be handled in convert_code()
            s += "=None"
        return s

class PariArgumentClass(PariArgument):
    """
    Class for arguments which are passed as a specific C/Cython class.

    The C/Cython type is given by ``self.ctype()``.
    """
    def prototype_code(self):
        """
        Return code to appear in the prototype of the Cython wrapper.
        """
        s = self.ctype() + " " + self.name
        if self.default is not None:
            s += "=" + self.default
        return s


class PariInstanceArgument(PariArgumentObject):
    """
    ``self`` argument for ``Pari`` object.

    This argument is never actually used.
    """
    def __init__(self):
        PariArgument.__init__(self, iter(["self"]), None, 0)
    def _typerepr(self):
        return "Pari"
    def ctype(self):
        return "GEN"


class PariArgumentGEN(PariArgumentObject):
    def _typerepr(self):
        return "GEN"
    def ctype(self):
        return "GEN"
    def convert_code(self):
        """
        Conversion to Gen
        """
        if self.index == 0:
            # self argument
            s  = ""
        elif self.default is None:
            s  = "        {name} = objtogen({name})\n"
        elif self.default is False:
            # This is actually a required argument
            # See parse_prototype() in parser.py why we need this
            s  = "        if {name} is None:\n"
            s += "            raise TypeError(\"missing required argument: '{name}'\")\n"
            s += "        {name} = objtogen({name})\n"
        else:
            s  = "        cdef bint _have_{name} = ({name} is not None)\n"
            s += "        if _have_{name}:\n"
            s += "            {name} = objtogen({name})\n"
        return s.format(name=self.name)
    def c_convert_code(self):
        """
        Conversion Gen -> GEN
        """
        if not self.default:
            # required argument
            s  = "        cdef GEN {tmp} = (<Gen>{name}).g\n"
        elif self.default == "NULL":
            s  = "        cdef GEN {tmp} = NULL\n"
            s += "        if _have_{name}:\n"
            s += "            {tmp} = (<Gen>{name}).g\n"
        elif self.default == "0":
            s  = "        cdef GEN {tmp} = gen_0\n"
            s += "        if _have_{name}:\n"
            s += "            {tmp} = (<Gen>{name}).g\n"
        else:
            raise ValueError("default value %r for GEN argument %r is not supported" % (self.default, self.name))
        return s.format(name=self.name, tmp=self.tmpname)
    def call_code(self):
        return self.tmpname

class PariArgumentString(PariArgumentObject):
    def _typerepr(self):
        return "str"
    def ctype(self):
        return "char *"
    def convert_code(self):
        if self.default is None:
            s  = "        {name} = to_bytes({name})\n"
            s += "        cdef char* {tmp} = <bytes>{name}\n"
        else:
            s  = "        cdef char* {tmp}\n"
            s += "        if {name} is None:\n"
            s += "            {tmp} = {default}\n"
            s += "        else:\n"
            s += "            {name} = to_bytes({name})\n"
            s += "            {tmp} = <bytes>{name}\n"
        return s.format(name=self.name, tmp=self.tmpname, default=self.default)
    def call_code(self):
        return self.tmpname

class PariArgumentVariable(PariArgumentObject):
    def _typerepr(self):
        return "var"
    def ctype(self):
        return "long"
    def default_default(self):
        return "-1"
    def convert_code(self):
        if self.default is None:
            s  = "        cdef long {tmp} = get_var({name})\n"
        else:
            s  = "        cdef long {tmp} = {default}\n"
            s += "        if {name} is not None:\n"
            s += "            {tmp} = get_var({name})\n"
        return s.format(name=self.name, tmp=self.tmpname, default=self.default)
    def call_code(self):
        return self.tmpname

class PariArgumentLong(PariArgumentClass):
    def _typerepr(self):
        return "long"
    def ctype(self):
        return "long"
    def default_default(self):
        return "0"

class PariArgumentULong(PariArgumentClass):
    def _typerepr(self):
        return "unsigned long"
    def ctype(self):
        return "unsigned long"
    def default_default(self):
        return "0"

class PariArgumentPrec(PariArgumentClass):
    def _typerepr(self):
        return "prec"
    def ctype(self):
        return "long"
    def always_default(self):
        return "0"
    def get_argument_name(self, namesiter):
        return "precision"
    def c_convert_code(self):
        s = "        {name} = prec_bits_to_words({name})\n"
        return s.format(name=self.name)

class PariArgumentBitprec(PariArgumentClass):
    def _typerepr(self):
        return "bitprec"
    def ctype(self):
        return "long"
    def always_default(self):
        return "0"
    def get_argument_name(self, namesiter):
        return "precision"
    def c_convert_code(self):
        s  = "        if not {name}:\n"
        s += "            {name} = default_bitprec()\n"
        return s.format(name=self.name)

class PariArgumentSeriesPrec(PariArgumentClass):
    def _typerepr(self):
        return "serprec"
    def ctype(self):
        return "long"
    def default_default(self):
        return "-1"
    def get_argument_name(self, namesiter):
        return "serprec"
    def c_convert_code(self):
        s  = "        if {name} < 0:\n"
        s += "            {name} = precdl  # Global PARI series precision\n"
        return s.format(name=self.name)


pari_arg_types = {
        'G': PariArgumentGEN,
        'W': PariArgumentGEN,
        'r': PariArgumentString,
        's': PariArgumentString,
        'L': PariArgumentLong,
        'U': PariArgumentULong,
        'n': PariArgumentVariable,
        'p': PariArgumentPrec,
        'b': PariArgumentBitprec,
        'P': PariArgumentSeriesPrec,

    # Codes which are known but not actually supported yet
        '&': None,
        'V': None,
        'I': None,
        'E': None,
        'J': None,
        'C': None,
        '*': None,
        '=': None}