From 4e0b2ec9c9f8783a82fa3079ee1e152eeb3e1af6 Mon Sep 17 00:00:00 2001
From: Daniele Varrazzo <daniele.varrazzo@gmail.com>
Date: Thu, 4 Oct 2018 16:13:46 +0100
Subject: [PATCH] Added Diagnostics.severity_nonlocalized attribute

Close #783.
---
 NEWS                       |  2 ++
 doc/src/extensions.rst     |  4 ++++
 psycopg/diagnostics_type.c | 14 ++++++++++++--
 tests/test_module.py       | 13 +++++++++++--
 4 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index eabb8392..8cbaa7f2 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ New features:
   (:ticket:`#773`).
 - `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
   maintain columns order (:ticket:`#177`).
+- Added `!severity_nonlocalized` attribute on the
+  `~psycopg2.extensions.Diagnostics` object (:ticket:`#783`).
 
 Other changes:
 
diff --git a/doc/src/extensions.rst b/doc/src/extensions.rst
index 34d53a7e..14a18ede 100644
--- a/doc/src/extensions.rst
+++ b/doc/src/extensions.rst
@@ -186,6 +186,7 @@ introspection etc.
         message_primary
         schema_name
         severity
+        severity_nonlocalized
         source_file
         source_function
         source_line
@@ -198,6 +199,9 @@ introspection etc.
         not all the fields are available for all the errors and for all the
         server versions.
 
+        .. versionadded:: 2.8
+            The `!severity_nonlocalized` attribute.
+
 
 
 .. _sql-adaptation-objects:
diff --git a/psycopg/diagnostics_type.c b/psycopg/diagnostics_type.c
index 0af65265..03fd07c1 100644
--- a/psycopg/diagnostics_type.c
+++ b/psycopg/diagnostics_type.c
@@ -29,8 +29,11 @@
 #include "psycopg/diagnostics.h"
 #include "psycopg/error.h"
 
-/* These are new in PostgreSQL 9.3. Defining them here so that psycopg2 can
- * use them with a 9.3+ server even if compiled against pre-9.3 headers. */
+
+/* These constants are defined in src/include/postgres_ext.h but some may not
+ * be available with the libpq we currently support at compile time. */
+
+/* Available from PG 9.3 */
 #ifndef PG_DIAG_SCHEMA_NAME
 #define PG_DIAG_SCHEMA_NAME     's'
 #endif
@@ -47,6 +50,11 @@
 #define PG_DIAG_CONSTRAINT_NAME 'n'
 #endif
 
+/* Available from PG 9.6 */
+#ifndef PG_DIAG_SEVERITY_NONLOCALIZED
+#define PG_DIAG_SEVERITY_NONLOCALIZED 'V'
+#endif
+
 
 /* Retrieve an error string from the exception's cursor.
  *
@@ -70,6 +78,8 @@ psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
 static struct PyGetSetDef diagnosticsObject_getsets[] = {
     { "severity", (getter)psyco_diagnostics_get_field, NULL,
       NULL, (void*) PG_DIAG_SEVERITY },
+    { "severity_nonlocalized", (getter)psyco_diagnostics_get_field, NULL,
+      NULL, (void*) PG_DIAG_SEVERITY_NONLOCALIZED },
     { "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
       NULL, (void*) PG_DIAG_SQLSTATE },
     { "message_primary", (getter)psyco_diagnostics_get_field, NULL,
diff --git a/tests/test_module.py b/tests/test_module.py
index ddd6b027..cf3eb020 100755
--- a/tests/test_module.py
+++ b/tests/test_module.py
@@ -173,8 +173,8 @@ class ExceptionsTestCase(ConnectingTestCase):
                 'column_name', 'constraint_name', 'context', 'datatype_name',
                 'internal_position', 'internal_query', 'message_detail',
                 'message_hint', 'message_primary', 'schema_name', 'severity',
-                'source_file', 'source_function', 'source_line', 'sqlstate',
-                'statement_position', 'table_name', ]:
+                'severity_nonlocalized', 'source_file', 'source_function',
+                'source_line', 'sqlstate', 'statement_position', 'table_name', ]:
             v = getattr(diag, attr)
             if v is not None:
                 self.assert_(isinstance(v, str))
@@ -276,6 +276,15 @@ class ExceptionsTestCase(ConnectingTestCase):
         self.assertEqual(e.diag.constraint_name, "chk_eq1")
         self.assertEqual(e.diag.datatype_name, None)
 
+    @skip_before_postgres(9, 6)
+    def test_9_6_diagnostics(self):
+        cur = self.conn.cursor()
+        try:
+            cur.execute("select 1 from nosuchtable")
+        except psycopg2.Error as exc:
+            e = exc
+        self.assertEqual(e.diag.severity_nonlocalized, 'ERROR')
+
     def test_pickle(self):
         import pickle
         cur = self.conn.cursor()