File: handle_error.pyx

package info (click to toggle)
sagemath 7.4-9
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 108,312 kB
  • ctags: 72,147
  • sloc: python: 800,328; sh: 10,775; cpp: 7,154; ansic: 2,301; objc: 1,372; makefile: 889; lisp: 1
file content (207 lines) | stat: -rw-r--r-- 5,736 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
"""
Functions for handling PARI errors

AUTHORS:

- Peter Bruin (September 2013): initial version (:trac:`9640`)

- Jeroen Demeyer (January 2015): use ``cb_pari_err_handle`` (:trac:`14894`)

"""

#*****************************************************************************
#       Copyright (C) 2013 Peter Bruin
#       Copyright (C) 2015 Jeroen Demeyer
#
# 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 print_function

from .paridecl cimport *
from .paripriv cimport *
include "cysignals/signals.pxi"

from cpython cimport PyErr_Occurred
from pari_instance cimport pari_instance


# We derive PariError from RuntimeError, for backward compatibility with
# code that catches the latter.
class PariError(RuntimeError):
    """
    Error raised by PARI
    """
    def errnum(self):
        r"""
        Return the PARI error number corresponding to this exception.

        EXAMPLES::

            sage: try:
            ....:     pari('1/0')
            ....: except PariError as err:
            ....:     print(err.errnum())
            31
        """
        return self.args[0]

    def errtext(self):
        """
        Return the message output by PARI when this error occurred.

        EXAMPLE::

            sage: try:
            ....:     pari('pi()')
            ....: except PariError as e:
            ....:     print(e.errtext())
            not a function in function call

        """
        return self.args[1]

    def errdata(self):
        """
        Return the error data (a ``t_ERROR`` gen) corresponding to this
        error.

        EXAMPLES::

            sage: try:
            ....:     pari(Mod(2,6))^-1
            ....: except PariError as e:
            ....:     E = e.errdata()
            sage: E
            error("impossible inverse in Fp_inv: Mod(2, 6).")
            sage: E.component(2)
            Mod(2, 6)
        """
        return self.args[2]

    def __repr__(self):
        r"""
        TESTS::

            sage: PariError(11)
            PariError(11)
        """
        return "PariError(%d)"%self.errnum()

    def __str__(self):
        r"""
        Return a suitable message for displaying this exception.

        This is simply the error text with certain trailing characters
        stripped.

        EXAMPLES::

            sage: try:
            ....:     pari('1/0')
            ....: except PariError as err:
            ....:     print(err)
            _/_: impossible inverse in gdiv: 0

        A syntax error::

            sage: pari('!@#$%^&*()')
            Traceback (most recent call last):
            ...
            PariError: syntax error, unexpected $undefined
        """
        return self.errtext().rstrip(" .:")


cdef void _pari_init_error_handling():
    """
    Set up our code for handling PARI errors.

    TESTS::

        sage: try:
        ....:     p = pari.polcyclo(-1)
        ....: except PariError as e:
        ....:     print(e.errtext())
        domain error in polcyclo: index <= 0

    Warnings still work just like in GP::

        sage: pari('warning("test")')
          ***   user warning: test
    """
    global cb_pari_err_handle
    global cb_pari_err_recover
    cb_pari_err_handle = _pari_err_handle
    cb_pari_err_recover = _pari_err_recover


cdef int _pari_err_handle(GEN E) except 0:
    """
    Convert a PARI error into a Sage exception, unless the error was
    a stack overflow, in which case we enlarge the stack.

    This function is a callback from the PARI error handler.

    EXAMPLES::

        sage: pari('error("test")')
        Traceback (most recent call last):
        ...
        PariError: error: user error: test
        sage: pari(1)/pari(0)
        Traceback (most recent call last):
        ...
        PariError: impossible inverse in gdiv: 0

    """
    cdef long errnum = E[1]

    sig_block()
    cdef char* errstr
    cdef char* s
    try:
        if errnum == e_STACK:
            # Custom error message for PARI stack overflow
            pari_error_string = "the PARI stack overflows (current size: {}; maximum size: {})\n"
            pari_error_string += "You can use pari.allocatemem() to change the stack size and try again"
            pari_error_string = pari_error_string.format(pari_mainstack.size, pari_mainstack.vsize)
        else:
            errstr = pari_err2str(E)
            pari_error_string = errstr.decode('ascii')
            pari_free(errstr)

        s = closure_func_err()
        if s is not NULL:
            pari_error_string = s.decode('ascii') + ": " + pari_error_string

        raise PariError(errnum, pari_error_string, pari_instance.new_gen_noclear(E))
    finally:
        sig_unblock()


cdef void _pari_err_recover(long errnum):
    """
    Reset the error string and jump back to ``sig_on()``, either to
    retry the code (in case of no error) or to make the already-raised
    exception known to Python.

    TEST:

    Perform a computation that requires doubling the default stack
    several times::

        sage: pari.allocatemem(2^12, 2^26)
        PARI stack size set to 4096 bytes, maximum size set to 67108864
        sage: x = pari('2^(2^26)')
          *** _^_: Warning: increasing stack size to...
        sage: x == 2^(2^26)
        True

    """
    # An exception was raised.  Jump to the signal-handling code
    # which will cause sig_on() to see the exception.
    sig_error()