From: Peter Eisentraut Date: Sat, 16 Jan 2010 11:03:51 +0000 (+0000) Subject: Improved printing of Python exceptions in PL/Python X-Git-Tag: REL9_0_ALPHA4~243 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=44e03742d87ea518990ce9e20bee53c239456f20;p=postgresql Improved printing of Python exceptions in PL/Python Mimic the Python interpreter's own logic for printing exceptions instead of just using the straight str() call, so that you get plpy.SPIError instead of and for built-in exceptions merely UnicodeEncodeError Besides looking better this cuts down on the endless version differences in the regression test expected files. --- diff --git a/src/pl/plpython/expected/README b/src/pl/plpython/expected/README index a187937540..4173274a60 100644 --- a/src/pl/plpython/expected/README +++ b/src/pl/plpython/expected/README @@ -1,12 +1,8 @@ Guide to alternative expected files: -plpython_error_2.out Python 2.2, 2.3, 2.4 -plpython_error.out Python 2.5, 2.6 - plpython_unicode.out any version, when server encoding != SQL_ASCII and client encoding = UTF8; else ... plpython_unicode_0.out any version, when server encoding != SQL_ASCII and client encoding != UTF8; else ... plpython_unicode_2.out Python 2.2 -plpython_unicode_3.out Python 2.3, 2.4 -plpython_unicode_5.out Python 2.5, 2.6 +plpython_unicode_3.out Python 2.3, 2.4, 2.5, 2.6 plpython_types_3.out Python 3.1 diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out index 58f7b3a766..36ffa8b5bd 100644 --- a/src/pl/plpython/expected/plpython_error.out +++ b/src/pl/plpython/expected/plpython_error.out @@ -8,7 +8,7 @@ CREATE FUNCTION sql_syntax_error() RETURNS text 'plpy.execute("syntax error")' LANGUAGE plpythonu; SELECT sql_syntax_error(); -WARNING: PL/Python: : unrecognized error in PLy_spi_execute_query +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_execute_query CONTEXT: PL/Python function "sql_syntax_error" ERROR: syntax error at or near "syntax" LINE 1: syntax error @@ -23,7 +23,7 @@ CREATE FUNCTION exception_index_invalid(text) RETURNS text LANGUAGE plpythonu; SELECT exception_index_invalid('test'); ERROR: PL/Python: PL/Python function "exception_index_invalid" failed -DETAIL: : list index out of range +DETAIL: IndexError: list index out of range CONTEXT: PL/Python function "exception_index_invalid" /* check handling of nested exceptions */ @@ -33,7 +33,7 @@ CREATE FUNCTION exception_index_invalid_nested() RETURNS text return rv[0]' LANGUAGE plpythonu; SELECT exception_index_invalid_nested(); -WARNING: PL/Python: : unrecognized error in PLy_spi_execute_query +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_execute_query CONTEXT: PL/Python function "exception_index_invalid_nested" ERROR: function test5(unknown) does not exist LINE 1: SELECT test5('foo') @@ -55,7 +55,7 @@ return None ' LANGUAGE plpythonu; SELECT invalid_type_uncaught('rick'); -WARNING: PL/Python: : unrecognized error in PLy_spi_prepare +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_uncaught" ERROR: type "test" does not exist CONTEXT: PL/Python function "invalid_type_uncaught" @@ -78,7 +78,7 @@ return None ' LANGUAGE plpythonu; SELECT invalid_type_caught('rick'); -WARNING: PL/Python: : unrecognized error in PLy_spi_prepare +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_caught" ERROR: type "test" does not exist CONTEXT: PL/Python function "invalid_type_caught" @@ -100,7 +100,7 @@ return None ' LANGUAGE plpythonu; SELECT invalid_type_reraised('rick'); -WARNING: PL/Python: : unrecognized error in PLy_spi_prepare +WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare CONTEXT: PL/Python function "invalid_type_reraised" ERROR: type "test" does not exist CONTEXT: PL/Python function "invalid_type_reraised" diff --git a/src/pl/plpython/expected/plpython_error_2.out b/src/pl/plpython/expected/plpython_error_2.out deleted file mode 100644 index bcbf2e655c..0000000000 --- a/src/pl/plpython/expected/plpython_error_2.out +++ /dev/null @@ -1,124 +0,0 @@ --- test error handling, i forgot to restore Warn_restart in --- the trigger handler once. the errors and subsequent core dump were --- interesting. -/* Flat out syntax error - */ -CREATE FUNCTION sql_syntax_error() RETURNS text - AS -'plpy.execute("syntax error")' - LANGUAGE plpythonu; -SELECT sql_syntax_error(); -WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_execute_query -CONTEXT: PL/Python function "sql_syntax_error" -ERROR: syntax error at or near "syntax" -LINE 1: syntax error - ^ -QUERY: syntax error -CONTEXT: PL/Python function "sql_syntax_error" -/* check the handling of uncaught python exceptions - */ -CREATE FUNCTION exception_index_invalid(text) RETURNS text - AS -'return args[1]' - LANGUAGE plpythonu; -SELECT exception_index_invalid('test'); -ERROR: PL/Python: PL/Python function "exception_index_invalid" failed -DETAIL: exceptions.IndexError: list index out of range -CONTEXT: PL/Python function "exception_index_invalid" -/* check handling of nested exceptions - */ -CREATE FUNCTION exception_index_invalid_nested() RETURNS text - AS -'rv = plpy.execute("SELECT test5(''foo'')") -return rv[0]' - LANGUAGE plpythonu; -SELECT exception_index_invalid_nested(); -WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_execute_query -CONTEXT: PL/Python function "exception_index_invalid_nested" -ERROR: function test5(unknown) does not exist -LINE 1: SELECT test5('foo') - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -QUERY: SELECT test5('foo') -CONTEXT: PL/Python function "exception_index_invalid_nested" -/* a typo - */ -CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text - AS -'if "plan" not in SD: - q = "SELECT fname FROM users WHERE lname = $1" - SD["plan"] = plpy.prepare(q, [ "test" ]) -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpythonu; -SELECT invalid_type_uncaught('rick'); -WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare -CONTEXT: PL/Python function "invalid_type_uncaught" -ERROR: type "test" does not exist -CONTEXT: PL/Python function "invalid_type_uncaught" -/* for what it's worth catch the exception generated by - * the typo, and return None - */ -CREATE FUNCTION invalid_type_caught(a text) RETURNS text - AS -'if "plan" not in SD: - q = "SELECT fname FROM users WHERE lname = $1" - try: - SD["plan"] = plpy.prepare(q, [ "test" ]) - except plpy.SPIError, ex: - plpy.notice(str(ex)) - return None -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpythonu; -SELECT invalid_type_caught('rick'); -WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare -CONTEXT: PL/Python function "invalid_type_caught" -ERROR: type "test" does not exist -CONTEXT: PL/Python function "invalid_type_caught" -/* for what it's worth catch the exception generated by - * the typo, and reraise it as a plain error - */ -CREATE FUNCTION invalid_type_reraised(a text) RETURNS text - AS -'if "plan" not in SD: - q = "SELECT fname FROM users WHERE lname = $1" - try: - SD["plan"] = plpy.prepare(q, [ "test" ]) - except plpy.SPIError, ex: - plpy.error(str(ex)) -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpythonu; -SELECT invalid_type_reraised('rick'); -WARNING: PL/Python: plpy.SPIError: unrecognized error in PLy_spi_prepare -CONTEXT: PL/Python function "invalid_type_reraised" -ERROR: type "test" does not exist -CONTEXT: PL/Python function "invalid_type_reraised" -/* no typo no messing about - */ -CREATE FUNCTION valid_type(a text) RETURNS text - AS -'if "plan" not in SD: - SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ]) -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpythonu; -SELECT valid_type('rick'); - valid_type ------------- - -(1 row) - diff --git a/src/pl/plpython/expected/plpython_unicode_2.out b/src/pl/plpython/expected/plpython_unicode_2.out index 5e44e4a799..d6bd823db8 100644 --- a/src/pl/plpython/expected/plpython_unicode_2.out +++ b/src/pl/plpython/expected/plpython_unicode_2.out @@ -25,12 +25,12 @@ return rv[0]["testvalue"] ' LANGUAGE plpythonu; SELECT unicode_return(); ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) +DETAIL: UnicodeError: ASCII encoding error: ordinal not in range(128) CONTEXT: while creating return value PL/Python function "unicode_return" INSERT INTO unicode_test (testvalue) VALUES ('test'); ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) +DETAIL: UnicodeError: ASCII encoding error: ordinal not in range(128) CONTEXT: while modifying trigger row PL/Python function "unicode_trigger" SELECT * FROM unicode_test; @@ -42,7 +42,7 @@ SELECT unicode_plan1(); WARNING: PL/Python: plpy.Error: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan1" ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) +DETAIL: UnicodeError: ASCII encoding error: ordinal not in range(128) CONTEXT: PL/Python function "unicode_plan1" SELECT unicode_plan2(); unicode_plan2 diff --git a/src/pl/plpython/expected/plpython_unicode_3.out b/src/pl/plpython/expected/plpython_unicode_3.out index 8e48c20a62..676845de4d 100644 --- a/src/pl/plpython/expected/plpython_unicode_3.out +++ b/src/pl/plpython/expected/plpython_unicode_3.out @@ -25,12 +25,12 @@ return rv[0]["testvalue"] ' LANGUAGE plpythonu; SELECT unicode_return(); ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +DETAIL: UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) CONTEXT: while creating return value PL/Python function "unicode_return" INSERT INTO unicode_test (testvalue) VALUES ('test'); ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +DETAIL: UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) CONTEXT: while modifying trigger row PL/Python function "unicode_trigger" SELECT * FROM unicode_test; @@ -42,7 +42,7 @@ SELECT unicode_plan1(); WARNING: PL/Python: plpy.Error: unrecognized error in PLy_spi_execute_plan CONTEXT: PL/Python function "unicode_plan1" ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +DETAIL: UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) CONTEXT: PL/Python function "unicode_plan1" SELECT unicode_plan2(); unicode_plan2 diff --git a/src/pl/plpython/expected/plpython_unicode_5.out b/src/pl/plpython/expected/plpython_unicode_5.out deleted file mode 100644 index 76ff8f23cd..0000000000 --- a/src/pl/plpython/expected/plpython_unicode_5.out +++ /dev/null @@ -1,52 +0,0 @@ --- --- Unicode handling --- -CREATE TABLE unicode_test ( - testvalue text NOT NULL -); -CREATE FUNCTION unicode_return() RETURNS text AS E' -return u"\\x80" -' LANGUAGE plpythonu; -CREATE FUNCTION unicode_trigger() RETURNS trigger AS E' -TD["new"]["testvalue"] = u"\\x80" -return "MODIFY" -' LANGUAGE plpythonu; -CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test - FOR EACH ROW EXECUTE PROCEDURE unicode_trigger(); -CREATE FUNCTION unicode_plan1() RETURNS text AS E' -plan = plpy.prepare("SELECT $1 AS testvalue", ["text"]) -rv = plpy.execute(plan, [u"\\x80"], 1) -return rv[0]["testvalue"] -' LANGUAGE plpythonu; -CREATE FUNCTION unicode_plan2() RETURNS text AS E' -plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", u"text"]) -rv = plpy.execute(plan, ["foo", "bar"], 1) -return rv[0]["testvalue"] -' LANGUAGE plpythonu; -SELECT unicode_return(); -ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: : 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) -CONTEXT: while creating return value -PL/Python function "unicode_return" -INSERT INTO unicode_test (testvalue) VALUES ('test'); -ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: : 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) -CONTEXT: while modifying trigger row -PL/Python function "unicode_trigger" -SELECT * FROM unicode_test; - testvalue ------------ -(0 rows) - -SELECT unicode_plan1(); -WARNING: PL/Python: : unrecognized error in PLy_spi_execute_plan -CONTEXT: PL/Python function "unicode_plan1" -ERROR: PL/Python: could not convert Python Unicode object to PostgreSQL server encoding -DETAIL: : 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) -CONTEXT: PL/Python function "unicode_plan1" -SELECT unicode_plan2(); - unicode_plan2 ---------------- - foobar -(1 row) - diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 085f0ea8d7..8ddb08c202 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,7 +1,7 @@ /********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.134 2009/12/15 22:59:54 petere Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.135 2010/01/16 11:03:51 petere Exp $ * ********************************************************************* */ @@ -3453,10 +3453,12 @@ PLy_traceback(int *xlevel) PyObject *e, *v, *tb; - PyObject *eob, - *vob = NULL; - char *vstr, - *estr; + PyObject *e_type_o; + PyObject *e_module_o; + char *e_type_s = NULL; + char *e_module_s = NULL; + PyObject *vob = NULL; + char *vstr; StringInfoData xstr; /* @@ -3476,23 +3478,39 @@ PLy_traceback(int *xlevel) PyErr_NormalizeException(&e, &v, &tb); Py_XDECREF(tb); - eob = PyObject_Str(e); + e_type_o = PyObject_GetAttrString(e, "__name__"); + e_module_o = PyObject_GetAttrString(e, "__module__"); + if (e_type_o) + e_type_s = PyString_AsString(e_type_o); + if (e_type_s) + e_module_s = PyString_AsString(e_module_o); + if (v && ((vob = PyObject_Str(v)) != NULL)) vstr = PyString_AsString(vob); else vstr = "unknown"; - /* - * I'm not sure what to do if eob is NULL here -- we can't call PLy_elog - * because that function calls us, so we could end up with infinite - * recursion. I'm not even sure if eob could be NULL here -- would an - * Assert() be more appropriate? - */ - estr = eob ? PyString_AsString(eob) : "unrecognized exception"; initStringInfo(&xstr); - appendStringInfo(&xstr, "%s: %s", estr, vstr); + if (!e_type_s || !e_module_s) + { + if (PyString_Check(e)) + /* deprecated string exceptions */ + appendStringInfoString(&xstr, PyString_AsString(e)); + else + /* shouldn't happen */ + appendStringInfoString(&xstr, "unrecognized exception"); + } + /* mimics behavior of traceback.format_exception_only */ + else if (strcmp(e_module_s, "builtins") == 0 + || strcmp(e_module_s, "__main__") == 0 + || strcmp(e_module_s, "exceptions") == 0) + appendStringInfo(&xstr, "%s", e_type_s); + else + appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s); + appendStringInfo(&xstr, ": %s", vstr); - Py_DECREF(eob); + Py_XDECREF(e_type_o); + Py_XDECREF(e_module_o); Py_XDECREF(vob); Py_XDECREF(v);