Age Owner TLA Line data Source code
1 : /*
2 : * reporting Python exceptions as PostgreSQL errors
3 : *
4 : * src/pl/plpython/plpy_elog.c
5 : */
6 :
7 : #include "postgres.h"
8 :
9 : #include "lib/stringinfo.h"
10 : #include "plpy_elog.h"
11 : #include "plpy_main.h"
12 : #include "plpy_procedure.h"
13 : #include "plpython.h"
14 :
15 : PyObject *PLy_exc_error = NULL;
16 : PyObject *PLy_exc_fatal = NULL;
17 : PyObject *PLy_exc_spi_error = NULL;
18 :
19 :
20 : static void PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
21 : char **xmsg, char **tbmsg, int *tb_depth);
22 : static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
23 : char **hint, char **query, int *position,
24 : char **schema_name, char **table_name, char **column_name,
25 : char **datatype_name, char **constraint_name);
26 : static void PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail,
27 : char **hint, char **schema_name, char **table_name, char **column_name,
28 : char **datatype_name, char **constraint_name);
29 : static char *get_source_line(const char *src, int lineno);
30 :
31 : static void get_string_attr(PyObject *obj, char *attrname, char **str);
32 : static bool set_string_attr(PyObject *obj, char *attrname, char *str);
33 :
34 : /*
35 : * Emit a PG error or notice, together with any available info about
36 : * the current Python error, previously set by PLy_exception_set().
37 : * This should be used to propagate Python errors into PG. If fmt is
38 : * NULL, the Python error becomes the primary error message, otherwise
39 : * it becomes the detail. If there is a Python traceback, it is put
40 : * in the context.
41 : */
42 : void
2056 peter_e 43 CBC 57 : PLy_elog_impl(int elevel, const char *fmt,...)
44 : {
1656 tgl 45 57 : int save_errno = errno;
46 : char *xmsg;
47 : char *tbmsg;
48 : int tb_depth;
49 : StringInfoData emsg;
50 : PyObject *exc,
51 : *val,
52 : *tb;
4130 peter_e 53 57 : const char *primary = NULL;
3955 bruce 54 57 : int sqlerrcode = 0;
4130 peter_e 55 57 : char *detail = NULL;
56 57 : char *hint = NULL;
57 57 : char *query = NULL;
58 57 : int position = 0;
2557 teodor 59 57 : char *schema_name = NULL;
60 57 : char *table_name = NULL;
61 57 : char *column_name = NULL;
62 57 : char *datatype_name = NULL;
63 57 : char *constraint_name = NULL;
64 :
4130 peter_e 65 57 : PyErr_Fetch(&exc, &val, &tb);
66 :
67 57 : if (exc != NULL)
68 : {
2555 tgl 69 57 : PyErr_NormalizeException(&exc, &val, &tb);
70 :
4130 peter_e 71 57 : if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
2555 tgl 72 22 : PLy_get_spi_error_data(val, &sqlerrcode,
73 : &detail, &hint, &query, &position,
74 : &schema_name, &table_name, &column_name,
75 : &datatype_name, &constraint_name);
2557 teodor 76 35 : else if (PyErr_GivenExceptionMatches(val, PLy_exc_error))
77 13 : PLy_get_error_data(val, &sqlerrcode, &detail, &hint,
78 : &schema_name, &table_name, &column_name,
79 : &datatype_name, &constraint_name);
4130 peter_e 80 22 : else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
4130 peter_e 81 UBC 0 : elevel = FATAL;
82 : }
83 :
84 : /* this releases our refcount on tb! */
2555 tgl 85 CBC 57 : PLy_traceback(exc, val, tb,
86 : &xmsg, &tbmsg, &tb_depth);
87 :
4130 peter_e 88 57 : if (fmt)
89 : {
90 3 : initStringInfo(&emsg);
91 : for (;;)
4130 peter_e 92 UBC 0 : {
93 : va_list ap;
94 : int needed;
95 :
1656 tgl 96 CBC 3 : errno = save_errno;
4130 peter_e 97 3 : va_start(ap, fmt);
3454 tgl 98 3 : needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
4130 peter_e 99 3 : va_end(ap);
3454 tgl 100 3 : if (needed == 0)
4130 peter_e 101 3 : break;
3454 tgl 102 UBC 0 : enlargeStringInfo(&emsg, needed);
103 : }
4130 peter_e 104 CBC 3 : primary = emsg.data;
105 :
106 : /* Since we have a format string, we cannot have a SPI detail. */
107 3 : Assert(detail == NULL);
108 :
109 : /* If there's an exception message, it goes in the detail. */
110 3 : if (xmsg)
111 3 : detail = xmsg;
112 : }
113 : else
114 : {
115 54 : if (xmsg)
116 54 : primary = xmsg;
117 : }
118 :
119 57 : PG_TRY();
120 : {
121 57 : ereport(elevel,
122 : (errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
123 : errmsg_internal("%s", primary ? primary : "no exception data"),
124 : (detail) ? errdetail_internal("%s", detail) : 0,
125 : (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
126 : (hint) ? errhint("%s", hint) : 0,
127 : (query) ? internalerrquery(query) : 0,
128 : (position) ? internalerrposition(position) : 0,
129 : (schema_name) ? err_generic_string(PG_DIAG_SCHEMA_NAME,
130 : schema_name) : 0,
131 : (table_name) ? err_generic_string(PG_DIAG_TABLE_NAME,
132 : table_name) : 0,
133 : (column_name) ? err_generic_string(PG_DIAG_COLUMN_NAME,
134 : column_name) : 0,
135 : (datatype_name) ? err_generic_string(PG_DIAG_DATATYPE_NAME,
136 : datatype_name) : 0,
137 : (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME,
138 : constraint_name) : 0));
139 : }
1255 peter 140 57 : PG_FINALLY();
141 : {
4130 peter_e 142 57 : if (fmt)
143 3 : pfree(emsg.data);
144 57 : if (xmsg)
145 57 : pfree(xmsg);
146 57 : if (tbmsg)
147 57 : pfree(tbmsg);
2555 tgl 148 57 : Py_XDECREF(exc);
149 57 : Py_XDECREF(val);
150 : }
4130 peter_e 151 57 : PG_END_TRY();
4130 peter_e 152 UBC 0 : }
153 :
154 : /*
155 : * Extract a Python traceback from the given exception data.
156 : *
157 : * The exception error message is returned in xmsg, the traceback in
158 : * tbmsg (both as palloc'd strings) and the traceback depth in
159 : * tb_depth.
160 : *
161 : * We release refcounts on all the Python objects in the traceback stack,
162 : * but not on e or v.
163 : */
164 : static void
2555 tgl 165 CBC 57 : PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
166 : char **xmsg, char **tbmsg, int *tb_depth)
167 : {
168 : PyObject *e_type_o;
169 : PyObject *e_module_o;
4130 peter_e 170 57 : char *e_type_s = NULL;
171 57 : char *e_module_s = NULL;
172 57 : PyObject *vob = NULL;
173 : char *vstr;
174 : StringInfoData xstr;
175 : StringInfoData tbstr;
176 :
177 : /*
178 : * if no exception, return nulls
179 : */
180 57 : if (e == NULL)
181 : {
4130 peter_e 182 UBC 0 : *xmsg = NULL;
183 0 : *tbmsg = NULL;
184 0 : *tb_depth = 0;
185 :
186 0 : return;
187 : }
188 :
189 : /*
190 : * Format the exception and its value and put it in xmsg.
191 : */
192 :
4130 peter_e 193 CBC 57 : e_type_o = PyObject_GetAttrString(e, "__name__");
194 57 : e_module_o = PyObject_GetAttrString(e, "__module__");
195 57 : if (e_type_o)
398 andres 196 57 : e_type_s = PLyUnicode_AsString(e_type_o);
4130 peter_e 197 57 : if (e_type_s)
398 andres 198 57 : e_module_s = PLyUnicode_AsString(e_module_o);
199 :
4130 peter_e 200 57 : if (v && ((vob = PyObject_Str(v)) != NULL))
398 andres 201 57 : vstr = PLyUnicode_AsString(vob);
202 : else
4130 peter_e 203 UBC 0 : vstr = "unknown";
204 :
4130 peter_e 205 CBC 57 : initStringInfo(&xstr);
206 57 : if (!e_type_s || !e_module_s)
207 : {
208 : /* shouldn't happen */
398 andres 209 UBC 0 : appendStringInfoString(&xstr, "unrecognized exception");
210 : }
211 : /* mimics behavior of traceback.format_exception_only */
4130 peter_e 212 CBC 57 : else if (strcmp(e_module_s, "builtins") == 0
213 35 : || strcmp(e_module_s, "__main__") == 0
214 35 : || strcmp(e_module_s, "exceptions") == 0)
906 drowley 215 22 : appendStringInfoString(&xstr, e_type_s);
216 : else
4130 peter_e 217 35 : appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
218 57 : appendStringInfo(&xstr, ": %s", vstr);
219 :
220 57 : *xmsg = xstr.data;
221 :
222 : /*
223 : * Now format the traceback and put it in tbmsg.
224 : */
225 :
226 57 : *tb_depth = 0;
227 57 : initStringInfo(&tbstr);
228 : /* Mimic Python traceback reporting as close as possible. */
229 57 : appendStringInfoString(&tbstr, "Traceback (most recent call last):");
230 179 : while (tb != NULL && tb != Py_None)
231 : {
232 122 : PyObject *volatile tb_prev = NULL;
233 122 : PyObject *volatile frame = NULL;
234 122 : PyObject *volatile code = NULL;
235 122 : PyObject *volatile name = NULL;
236 122 : PyObject *volatile lineno = NULL;
237 122 : PyObject *volatile filename = NULL;
238 :
239 122 : PG_TRY();
240 : {
241 122 : lineno = PyObject_GetAttrString(tb, "tb_lineno");
242 122 : if (lineno == NULL)
4130 peter_e 243 UBC 0 : elog(ERROR, "could not get line number from Python traceback");
244 :
4130 peter_e 245 CBC 122 : frame = PyObject_GetAttrString(tb, "tb_frame");
246 122 : if (frame == NULL)
4130 peter_e 247 UBC 0 : elog(ERROR, "could not get frame from Python traceback");
248 :
4130 peter_e 249 CBC 122 : code = PyObject_GetAttrString(frame, "f_code");
250 122 : if (code == NULL)
4130 peter_e 251 UBC 0 : elog(ERROR, "could not get code object from Python frame");
252 :
4130 peter_e 253 CBC 122 : name = PyObject_GetAttrString(code, "co_name");
254 122 : if (name == NULL)
4130 peter_e 255 UBC 0 : elog(ERROR, "could not get function name from Python code object");
256 :
4130 peter_e 257 CBC 122 : filename = PyObject_GetAttrString(code, "co_filename");
258 122 : if (filename == NULL)
4130 peter_e 259 UBC 0 : elog(ERROR, "could not get file name from Python code object");
260 : }
261 0 : PG_CATCH();
262 : {
263 0 : Py_XDECREF(frame);
264 0 : Py_XDECREF(code);
265 0 : Py_XDECREF(name);
266 0 : Py_XDECREF(lineno);
267 0 : Py_XDECREF(filename);
268 0 : PG_RE_THROW();
269 : }
4130 peter_e 270 CBC 122 : PG_END_TRY();
271 :
272 : /* The first frame always points at <module>, skip it. */
273 122 : if (*tb_depth > 0)
274 : {
3955 bruce 275 68 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
276 : char *proname;
277 : char *fname;
278 : char *line;
279 : char *plain_filename;
280 : long plain_lineno;
281 :
282 : /*
283 : * The second frame points at the internal function, but to mimic
284 : * Python error reporting we want to say <module>.
285 : */
4130 peter_e 286 68 : if (*tb_depth == 1)
287 54 : fname = "<module>";
288 : else
398 andres 289 14 : fname = PLyUnicode_AsString(name);
290 :
4044 tgl 291 68 : proname = PLy_procedure_name(exec_ctx->curr_proc);
398 andres 292 68 : plain_filename = PLyUnicode_AsString(filename);
293 68 : plain_lineno = PyLong_AsLong(lineno);
294 :
4130 peter_e 295 68 : if (proname == NULL)
1165 alvherre 296 13 : appendStringInfo(&tbstr, "\n PL/Python anonymous code block, line %ld, in %s",
297 : plain_lineno - 1, fname);
298 : else
299 55 : appendStringInfo(&tbstr, "\n PL/Python function \"%s\", line %ld, in %s",
300 : proname, plain_lineno - 1, fname);
301 :
302 : /*
303 : * function code object was compiled with "<string>" as the
304 : * filename
305 : */
4044 tgl 306 68 : if (exec_ctx->curr_proc && plain_filename != NULL &&
4130 peter_e 307 68 : strcmp(plain_filename, "<string>") == 0)
308 : {
309 : /*
310 : * If we know the current procedure, append the exact line
311 : * from the source, again mimicking Python's traceback.py
312 : * module behavior. We could store the already line-split
313 : * source to avoid splitting it every time, but producing a
314 : * traceback is not the most important scenario to optimize
315 : * for. But we do not go as far as traceback.py in reading
316 : * the source of imported modules.
317 : */
4044 tgl 318 68 : line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
4130 peter_e 319 68 : if (line)
320 : {
321 68 : appendStringInfo(&tbstr, "\n %s", line);
322 68 : pfree(line);
323 : }
324 : }
325 : }
326 :
327 122 : Py_DECREF(frame);
328 122 : Py_DECREF(code);
329 122 : Py_DECREF(name);
330 122 : Py_DECREF(lineno);
331 122 : Py_DECREF(filename);
332 :
333 : /* Release the current frame and go to the next one. */
334 122 : tb_prev = tb;
335 122 : tb = PyObject_GetAttrString(tb, "tb_next");
336 122 : Assert(tb_prev != Py_None);
337 122 : Py_DECREF(tb_prev);
338 122 : if (tb == NULL)
4130 peter_e 339 UBC 0 : elog(ERROR, "could not traverse Python traceback");
4130 peter_e 340 CBC 122 : (*tb_depth)++;
341 : }
342 :
343 : /* Return the traceback. */
344 57 : *tbmsg = tbstr.data;
345 :
346 57 : Py_XDECREF(e_type_o);
347 57 : Py_XDECREF(e_module_o);
348 57 : Py_XDECREF(vob);
349 : }
350 :
351 : /*
352 : * Extract error code from SPIError's sqlstate attribute.
353 : */
354 : static void
2557 teodor 355 17 : PLy_get_sqlerrcode(PyObject *exc, int *sqlerrcode)
356 : {
357 : PyObject *sqlstate;
358 : char *buffer;
359 :
3723 heikki.linnakangas 360 17 : sqlstate = PyObject_GetAttrString(exc, "sqlstate");
361 17 : if (sqlstate == NULL)
362 4 : return;
363 :
398 andres 364 13 : buffer = PLyUnicode_AsString(sqlstate);
3723 heikki.linnakangas 365 13 : if (strlen(buffer) == 5 &&
366 13 : strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
367 : {
368 13 : *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2],
369 : buffer[3], buffer[4]);
370 : }
371 :
372 13 : Py_DECREF(sqlstate);
373 : }
374 :
375 : /*
376 : * Extract the error data from a SPIError
377 : */
378 : static void
2557 teodor 379 22 : PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
380 : char **hint, char **query, int *position,
381 : char **schema_name, char **table_name,
382 : char **column_name,
383 : char **datatype_name, char **constraint_name)
384 : {
385 : PyObject *spidata;
386 :
4130 peter_e 387 22 : spidata = PyObject_GetAttrString(exc, "spidata");
388 :
3723 heikki.linnakangas 389 22 : if (spidata != NULL)
390 : {
2555 tgl 391 18 : PyArg_ParseTuple(spidata, "izzzizzzzz",
392 : sqlerrcode, detail, hint, query, position,
393 : schema_name, table_name, column_name,
394 : datatype_name, constraint_name);
395 : }
396 : else
397 : {
398 : /*
399 : * If there's no spidata, at least set the sqlerrcode. This can happen
400 : * if someone explicitly raises a SPI exception from Python code.
401 : */
2557 teodor 402 4 : PLy_get_sqlerrcode(exc, sqlerrcode);
403 : }
404 :
4130 peter_e 405 22 : Py_XDECREF(spidata);
406 22 : }
407 :
408 : /*
409 : * Extract the error data from an Error.
410 : *
411 : * Note: position and query attributes are never set for Error so, unlike
412 : * PLy_get_spi_error_data, this function doesn't return them.
413 : */
414 : static void
2557 teodor 415 13 : PLy_get_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint,
416 : char **schema_name, char **table_name, char **column_name,
417 : char **datatype_name, char **constraint_name)
418 : {
419 13 : PLy_get_sqlerrcode(exc, sqlerrcode);
420 13 : get_string_attr(exc, "detail", detail);
421 13 : get_string_attr(exc, "hint", hint);
422 13 : get_string_attr(exc, "schema_name", schema_name);
423 13 : get_string_attr(exc, "table_name", table_name);
424 13 : get_string_attr(exc, "column_name", column_name);
425 13 : get_string_attr(exc, "datatype_name", datatype_name);
426 13 : get_string_attr(exc, "constraint_name", constraint_name);
427 13 : }
428 :
429 : /*
430 : * Get the given source line as a palloc'd string
431 : */
432 : static char *
4130 peter_e 433 68 : get_source_line(const char *src, int lineno)
434 : {
435 68 : const char *s = NULL;
436 68 : const char *next = src;
437 68 : int current = 0;
438 :
439 : /* sanity check */
4044 tgl 440 68 : if (lineno <= 0)
4044 tgl 441 UBC 0 : return NULL;
442 :
4130 peter_e 443 CBC 488 : while (current < lineno)
444 : {
445 420 : s = next;
446 420 : next = strchr(s + 1, '\n');
447 420 : current++;
448 420 : if (next == NULL)
4130 peter_e 449 UBC 0 : break;
450 : }
451 :
4130 peter_e 452 CBC 68 : if (current != lineno)
4130 peter_e 453 UBC 0 : return NULL;
454 :
4130 peter_e 455 CBC 318 : while (*s && isspace((unsigned char) *s))
456 250 : s++;
457 :
458 68 : if (next == NULL)
4130 peter_e 459 UBC 0 : return pstrdup(s);
460 :
461 : /*
462 : * Sanity check, next < s if the line was all-whitespace, which should
463 : * never happen if Python reported a frame created on that line, but check
464 : * anyway.
465 : */
4130 peter_e 466 CBC 68 : if (next < s)
4130 peter_e 467 UBC 0 : return NULL;
468 :
4130 peter_e 469 CBC 68 : return pnstrdup(s, next - s);
470 : }
471 :
472 :
473 : /* call PyErr_SetString with a vprint interface and translation support */
474 : void
475 18 : PLy_exception_set(PyObject *exc, const char *fmt,...)
476 : {
477 : char buf[1024];
478 : va_list ap;
479 :
480 18 : va_start(ap, fmt);
481 18 : vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
482 18 : va_end(ap);
483 :
484 18 : PyErr_SetString(exc, buf);
485 18 : }
486 :
487 : /* same, with pluralized message */
488 : void
489 1 : PLy_exception_set_plural(PyObject *exc,
490 : const char *fmt_singular, const char *fmt_plural,
491 : unsigned long n,...)
492 : {
493 : char buf[1024];
494 : va_list ap;
495 :
496 1 : va_start(ap, n);
497 1 : vsnprintf(buf, sizeof(buf),
498 1 : dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
499 : ap);
500 1 : va_end(ap);
501 :
502 1 : PyErr_SetString(exc, buf);
503 1 : }
504 :
505 : /* set attributes of the given exception to details from ErrorData */
506 : void
2557 teodor 507 11 : PLy_exception_set_with_details(PyObject *excclass, ErrorData *edata)
508 : {
509 11 : PyObject *args = NULL;
510 11 : PyObject *error = NULL;
511 :
512 11 : args = Py_BuildValue("(s)", edata->message);
513 11 : if (!args)
2557 teodor 514 UBC 0 : goto failure;
515 :
516 : /* create a new exception with the error message as the parameter */
2557 teodor 517 CBC 11 : error = PyObject_CallObject(excclass, args);
518 11 : if (!error)
2557 teodor 519 UBC 0 : goto failure;
520 :
2557 teodor 521 CBC 11 : if (!set_string_attr(error, "sqlstate",
522 : unpack_sql_state(edata->sqlerrcode)))
2557 teodor 523 UBC 0 : goto failure;
524 :
2557 teodor 525 CBC 11 : if (!set_string_attr(error, "detail", edata->detail))
2557 teodor 526 UBC 0 : goto failure;
527 :
2557 teodor 528 CBC 11 : if (!set_string_attr(error, "hint", edata->hint))
2557 teodor 529 UBC 0 : goto failure;
530 :
2557 teodor 531 CBC 11 : if (!set_string_attr(error, "query", edata->internalquery))
2557 teodor 532 UBC 0 : goto failure;
533 :
2557 teodor 534 CBC 11 : if (!set_string_attr(error, "schema_name", edata->schema_name))
2557 teodor 535 UBC 0 : goto failure;
536 :
2557 teodor 537 CBC 11 : if (!set_string_attr(error, "table_name", edata->table_name))
2557 teodor 538 UBC 0 : goto failure;
539 :
2557 teodor 540 CBC 11 : if (!set_string_attr(error, "column_name", edata->column_name))
2557 teodor 541 UBC 0 : goto failure;
542 :
2557 teodor 543 CBC 11 : if (!set_string_attr(error, "datatype_name", edata->datatype_name))
2557 teodor 544 UBC 0 : goto failure;
545 :
2557 teodor 546 CBC 11 : if (!set_string_attr(error, "constraint_name", edata->constraint_name))
2557 teodor 547 UBC 0 : goto failure;
548 :
2557 teodor 549 CBC 11 : PyErr_SetObject(excclass, error);
550 :
551 11 : Py_DECREF(args);
552 11 : Py_DECREF(error);
553 :
554 11 : return;
555 :
2557 teodor 556 UBC 0 : failure:
557 0 : Py_XDECREF(args);
558 0 : Py_XDECREF(error);
559 :
560 0 : elog(ERROR, "could not convert error to Python exception");
561 : }
562 :
563 : /* get string value of an object attribute */
564 : static void
2557 teodor 565 CBC 91 : get_string_attr(PyObject *obj, char *attrname, char **str)
566 : {
567 : PyObject *val;
568 :
569 91 : val = PyObject_GetAttrString(obj, attrname);
570 91 : if (val != NULL && val != Py_None)
571 : {
398 andres 572 28 : *str = pstrdup(PLyUnicode_AsString(val));
573 : }
2557 teodor 574 91 : Py_XDECREF(val);
575 91 : }
576 :
577 : /* set an object attribute to a string value, returns true when the set was
578 : * successful
579 : */
580 : static bool
581 99 : set_string_attr(PyObject *obj, char *attrname, char *str)
582 : {
583 : int result;
584 : PyObject *val;
585 :
586 99 : if (str != NULL)
587 : {
398 andres 588 39 : val = PLyUnicode_FromString(str);
2557 teodor 589 39 : if (!val)
2557 teodor 590 UBC 0 : return false;
591 : }
592 : else
593 : {
2557 teodor 594 CBC 60 : val = Py_None;
595 60 : Py_INCREF(Py_None);
596 : }
597 :
598 99 : result = PyObject_SetAttrString(obj, attrname, val);
599 99 : Py_DECREF(val);
600 :
601 99 : return result != -1;
602 : }
|